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 001/253] [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 002/253] 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 003/253] 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 004/253] 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 005/253] 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 006/253] 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 007/253] 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 008/253] 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 009/253] 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 010/253] 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 011/253] 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 012/253] 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 013/253] 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 014/253] 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 015/253] 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 016/253] 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 017/253] 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 018/253] 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 019/253] 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 020/253] 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 021/253] 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 022/253] 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 023/253] 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 024/253] 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 025/253] 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 026/253] 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 027/253] 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 028/253] 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 029/253] 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 030/253] 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 031/253] 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 032/253] 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 033/253] 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 034/253] 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 035/253] 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 036/253] 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 037/253] 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 038/253] 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 039/253] 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 040/253] 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 041/253] 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 042/253] 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 043/253] 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 044/253] 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 045/253] 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 046/253] 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 047/253] 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 048/253] 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 049/253] 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 050/253] 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 051/253] 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 052/253] 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 053/253] 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 054/253] 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 055/253] 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 056/253] 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 057/253] 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 058/253] 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 059/253] 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 060/253] 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 061/253] 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 062/253] 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 063/253] 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 064/253] 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 065/253] 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 066/253] 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 067/253] [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 068/253] 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 069/253] 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 070/253] [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 071/253] 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 072/253] 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 073/253] 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 074/253] 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 075/253] 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 076/253] 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 077/253] 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 078/253] 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 079/253] 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 080/253] 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 081/253] [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 082/253] 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 083/253] 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 084/253] 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 085/253] 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 086/253] 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 087/253] 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 088/253] [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 089/253] 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 090/253] 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 091/253] 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 092/253] 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 093/253] 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 094/253] 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 095/253] 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 096/253] 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 097/253] 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 098/253] 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 099/253] 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 100/253] 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 101/253] 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 102/253] 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 103/253] 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 104/253] 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 105/253] 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 106/253] 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 107/253] 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 108/253] 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 109/253] 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 110/253] 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 111/253] 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 112/253] 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 113/253] 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 114/253] 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 115/253] 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 116/253] 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 117/253] 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 118/253] 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 119/253] 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 120/253] 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 121/253] 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 122/253] 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 123/253] 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 124/253] 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 125/253] 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 126/253] 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 127/253] 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 128/253] 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 129/253] 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 130/253] 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 131/253] [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 132/253] 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 133/253] 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 134/253] 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 135/253] 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 136/253] 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 137/253] 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 138/253] 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 139/253] 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 140/253] 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 141/253] 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 142/253] 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 143/253] 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 144/253] 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 145/253] [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 146/253] 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 147/253] 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 148/253] 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 149/253] 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 150/253] 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 151/253] 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 152/253] 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 153/253] 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 154/253] 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 155/253] 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 156/253] 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 157/253] 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 158/253] 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 159/253] 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 160/253] [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 161/253] 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 162/253] 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 163/253] 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 164/253] 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 165/253] 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 166/253] 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 167/253] 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 168/253] 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 169/253] 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 170/253] 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 171/253] 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 172/253] 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 173/253] 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 174/253] 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 175/253] 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 176/253] 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 177/253] 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 178/253] 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 179/253] 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 180/253] 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 181/253] 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 182/253] 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 183/253] 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 184/253] 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 185/253] 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 186/253] 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 187/253] 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 188/253] 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 189/253] 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 190/253] 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 191/253] 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 192/253] 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 193/253] 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 194/253] 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 195/253] 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 196/253] 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 197/253] 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 198/253] 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 199/253] 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 200/253] 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 201/253] 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 202/253] 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 203/253] 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 204/253] 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 205/253] 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 206/253] 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 207/253] 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 208/253] 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 209/253] 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 210/253] 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 211/253] 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 212/253] 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 213/253] 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 214/253] 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 215/253] 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 216/253] 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 217/253] 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 218/253] 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 219/253] 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 220/253] 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 221/253] 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 222/253] 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 223/253] 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 224/253] 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 225/253] 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 226/253] 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 227/253] 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 228/253] 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 229/253] 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 230/253] 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 231/253] 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 232/253] 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 233/253] 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 234/253] 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 235/253] 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 236/253] 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 237/253] 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 238/253] 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 239/253] 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 240/253] 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 241/253] 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 242/253] 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 243/253] 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 244/253] 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 245/253] 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 246/253] 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 247/253] 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 248/253] 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 249/253] 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 250/253] 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 251/253] 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 252/253] 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 253/253] 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