From 852eda0221c6cf32f23be3f676790aff29e7706f Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 6 Jun 2024 11:52:55 +0200 Subject: [PATCH 01/52] [5.1.3] Fix broken build with GCC 14 (#5363) * missing header * fix implicit int --- ext-src/php_swoole_call_stack.h | 4 ++-- thirdparty/php83/pdo_odbc/odbc_driver.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ext-src/php_swoole_call_stack.h b/ext-src/php_swoole_call_stack.h index 8c89f8305bc..639a303d7f4 100644 --- a/ext-src/php_swoole_call_stack.h +++ b/ext-src/php_swoole_call_stack.h @@ -18,8 +18,8 @@ #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/thirdparty/php83/pdo_odbc/odbc_driver.c b/thirdparty/php83/pdo_odbc/odbc_driver.c index c83da646cba..a5e3a8b4f0c 100644 --- a/thirdparty/php83/pdo_odbc/odbc_driver.c +++ b/thirdparty/php83/pdo_odbc/odbc_driver.c @@ -25,6 +25,7 @@ #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; From 25561e555ec6639d3fa0e787fcfe06ccced4d003 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 6 Jun 2024 17:53:13 +0800 Subject: [PATCH 02/52] [5.1] Next Version (#5364) * package.xml * next version --- CMakeLists.txt | 2 +- include/swoole_version.h | 6 +++--- package.xml | 25 ++++++++++++++++++++----- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73345cc3bab..6a03f7f8d82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.3) +set(SWOOLE_VERSION 5.1.4-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index f68abd7c63c..ddfce84379f 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 3 +#define SWOOLE_RELEASE_VERSION 4 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.3" -#define SWOOLE_VERSION_ID 50103 +#define SWOOLE_VERSION "5.1.4-dev" +#define SWOOLE_VERSION_ID 50104 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ diff --git a/package.xml b/package.xml index 6d5a026a4fa..dd40b1e7ec0 100644 --- a/package.xml +++ b/package.xml @@ -63,11 +63,26 @@ Apache2.0 - - Fixed crash in pdo_sqlite hook in version 8.3 - - Optimized HTTP COOKIE parsing to no longer throw PHP warnings, preventing them from being converted to exceptions - - Fixed issue where OpenSSL would not correctly return an empty string when the peer closed the connection - - Fixed incorrect setting of TCP_NODELAY - - Fixed issue with proc_open hook not supporting pty + - Fix the problem of being unable to install through pecl. + - Fix the issue of Swoole\Coroutine\FastCGI\Client client being unable to set keepalive. + - Fix the issue of process continuously restarting due to error thrown when request parameters exceed max_input_vars. + - Fix the unknown issue caused by using Swoole\Event::wait() in a coroutine. + - Fix the issue of proc_open not supporting pty when used in a coroutine. + - Fix the segmentation fault issue in pdo_sqlite on PHP 8.3. + - Fix the unnecessary warning when compiling Swoole. + - Fix the error thrown when calling zend_fetch_resource2_ex on closed STDOUT/STDERR. + - Fix the invalid set_tcp_nodelay configuration. + - Fix the occasional unreachable branch issue triggered during file uploads. + - Fix the issue causing PHP core to throw errors when dispatch_func is set. + - Fix the obsolete warning of AC_PROG_CC_C99 in autoconf >= 2.70 version. + - Remove unnecessary checks for socket structs. + - Upgrade the Swoole library. + - Add support for http status code 451 in Swoole\Http\Response. + - Synchronize file operation code across different versions of PHP. + - Synchronize PDO operation code across different versions of PHP. + - Optimize the code for Socket::ssl_recv() function. + - Optimized config.m4 by allowing some configurations to set dependency library locations using pkg-config. + - Optimize the issue with using dynamic arrays when parsing request headers. From 73c1b20fa5c17a5bb86e211ef27741397622e36c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 4 Jul 2024 11:20:35 +0800 Subject: [PATCH 03/52] Fix bug #5313 (#5395) --- ext-src/swoole_pgsql.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/ext-src/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 0c91350d2c5..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); @@ -190,7 +209,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); From 142fb27265d8c6e33a74e4b3262f4b3244373f4d Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 5 Jul 2024 16:44:58 +0800 Subject: [PATCH 04/52] [5.1]Fix bug #5313 (#5397) * 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 6e0b20f8de70c4aa5e64d3976e9f42c278a34d07 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 18 Jul 2024 19:20:07 +0800 Subject: [PATCH 05/52] 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 e5909cdd1df76a79d51049d7efd48891f4d1dfd3 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 24 Jul 2024 11:32:53 +0800 Subject: [PATCH 06/52] [5.1]Fix bug #5407 (#5414) * fix bug #5407 * optimize code --- src/server/manager.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/server/manager.cc b/src/server/manager.cc index 5c0bc36d09f..114c50bc97a 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -608,6 +608,12 @@ pid_t Server::spawn_event_worker(Worker *worker) { return pid; } + // see https://github.com/swoole/swoole-src/issues/5407 + if (worker->concurrency > 0 && worker_num > 1) { + sw_atomic_sub_fetch(&gs->concurrency, worker->concurrency); + worker->concurrency = 0; + } + if (is_base_mode()) { gs->connection_nums[worker->id] = 0; gs->event_workers.main_loop(&gs->event_workers, worker); From e65e6d049f97e3297c9acfdbf1bed0d7755fb2c1 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 7 Aug 2024 17:28:37 +0800 Subject: [PATCH 07/52] fix bug #5432 (#5435) --- src/server/manager.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/server/manager.cc b/src/server/manager.cc index 114c50bc97a..efe33394a68 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -614,6 +614,11 @@ pid_t Server::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 (is_base_mode()) { gs->connection_nums[worker->id] = 0; gs->event_workers.main_loop(&gs->event_workers, worker); From cf8c93d28b162defd5a2f3c4eced6530ed90bc71 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 22 Aug 2024 10:21:07 +0800 Subject: [PATCH 08/52] [5.1]optimize http response (#5445) * optimize http response * optimize code * optimize code * fix scope --- ext-src/php_swoole_private.h | 9 +- ext-src/swoole_http_response.cc | 210 +++++++++++++---------------- include/swoole_config.h | 1 + tests/include/api/curl_multi.php | 3 + tests/swoole_curl/ssl/version.phpt | 3 +- 5 files changed, 105 insertions(+), 121 deletions(-) diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index c762021e964..ed861839396 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -705,11 +705,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 9eb633d5e22..d4a663f5580 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -237,17 +237,12 @@ static PHP_METHOD(swoole_http_response, write) { } } - 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(E_WARNING, "data to send is empty"); RETURN_FALSE; - } else { - http_body.length = length; } // Why not enable compression? @@ -257,12 +252,12 @@ static PHP_METHOD(swoole_http_response, write) { // **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); + 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); @@ -291,72 +286,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); @@ -364,7 +354,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) { @@ -390,6 +382,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; @@ -419,15 +412,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: ")); @@ -440,6 +431,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); } @@ -480,9 +472,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")); } } @@ -752,16 +744,8 @@ bool HttpContext::send_file(const char *file, uint32_t l_file, off_t offset, siz } 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; - } + char *data = nullptr; + size_t length = zdata ? php_swoole_get_send_data(zdata, &data) : 0; if (send_chunked) { if (send_trailer_) { @@ -771,83 +755,77 @@ void HttpContext::end(zval *zdata, zval *return_value) { send_trailer(return_value); send_trailer_ = 0; } else { - if (!send(this, ZEND_STRL("0\r\n\r\n"))) { + if (!send(this, ZEND_STRL(SW_HTTP_CHUNK_EOF))) { RETURN_FALSE; } } send_chunked = 0; - } else { - String *http_buffer = get_write_buffer(); - http_buffer->clear(); + return; + } #ifdef SW_HAVE_ZLIB - if (upgrade) { - Server *serv = nullptr; - Connection *conn = nullptr; - if (!co_socket) { - serv = (Server *) private_data; - conn = serv->get_connection_verify(fd); - } - bool enable_websocket_compression = co_socket ? websocket_compression : serv->websocket_compression; - bool accept_websocket_compression = false; - zval *pData; - if (enable_websocket_compression && request.zobject && - (pData = zend_hash_str_find(Z_ARRVAL_P(request.zheader), ZEND_STRL("sec-websocket-extensions"))) && - Z_TYPE_P(pData) == IS_STRING) { - std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData)); - if (value.substr(0, value.find_first_of(';')) == "permessage-deflate") { - accept_websocket_compression = true; - set_header(ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false); - } - } - websocket_compression = accept_websocket_compression; - if (conn) { - conn->websocket_compression = accept_websocket_compression; + if (upgrade) { + Server *serv = nullptr; + Connection *conn = nullptr; + if (!co_socket) { + serv = (Server *) private_data; + conn = serv->get_connection_verify(fd); + } + bool enable_websocket_compression = co_socket ? websocket_compression : serv->websocket_compression; + bool accept_websocket_compression = false; + zval *pData; + if (enable_websocket_compression && request.zobject && + (pData = zend_hash_str_find(Z_ARRVAL_P(request.zheader), ZEND_STRL("sec-websocket-extensions"))) && + Z_TYPE_P(pData) == IS_STRING) { + std::string value(Z_STRVAL_P(pData), Z_STRLEN_P(pData)); + if (value.substr(0, value.find_first_of(';')) == "permessage-deflate") { + accept_websocket_compression = true; + set_header(ZEND_STRL("Sec-Websocket-Extensions"), ZEND_STRL(SW_WEBSOCKET_EXTENSION_DEFLATE), false); } } + websocket_compression = accept_websocket_compression; + if (conn) { + conn->websocket_compression = accept_websocket_compression; + } + } #endif - build_header(http_buffer, http_body.str, http_body.length); - - char *send_body_str; - size_t send_body_len; + String *http_buffer = get_write_buffer(); + http_buffer->clear(); + build_header(http_buffer, data, length); - 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 + if (content_compressed) { + data = zlib_buffer->str; + length = zlib_buffer->length; + } #endif - { - send_body_str = http_body.str; - send_body_len = http_body.length; + + if (length > SW_HTTP_MAX_APPEND_DATA) { + if (!send(this, http_buffer->str, http_buffer->length)) { + send_header_ = 0; + RETURN_FALSE; } - // 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 (!send(this, http_buffer->str, http_buffer->length)) { - send_header_ = 0; - RETURN_FALSE; - } - if (!send(this, send_body_str, send_body_len)) { - end_ = 1; - close(this); - RETURN_FALSE; - } - goto _skip_copy; + + 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; } } + } - if (!send(this, http_buffer->str, http_buffer->length)) { - end_ = 1; - close(this); - RETURN_FALSE; - } + if (!send(this, http_buffer->str, http_buffer->length)) { + end_ = 1; + close(this); + RETURN_FALSE; } _skip_copy: 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" diff --git a/tests/include/api/curl_multi.php b/tests/include/api/curl_multi.php index 30126dba6e0..f146e9fa018 100644 --- a/tests/include/api/curl_multi.php +++ b/tests/include/api/curl_multi.php @@ -8,14 +8,17 @@ function swoole_test_curl_multi($options = []) { function swoole_test_curl_multi_ex($mh, $options = []) { $ch1 = curl_init(); $ch2 = curl_init(); + $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0'; // 设置URL和相应的选项 curl_setopt($ch1, CURLOPT_URL, "https://www.baidu.com/"); curl_setopt($ch1, CURLOPT_HEADER, 0); + curl_setopt($ch2, CURLOPT_USERAGENT, $userAgent); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch2, CURLOPT_URL, "https://www.zhihu.com/"); curl_setopt($ch2, CURLOPT_HEADER, 0); + curl_setopt($ch2, CURLOPT_USERAGENT, $userAgent); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); $mh = curl_multi_init(); diff --git a/tests/swoole_curl/ssl/version.phpt b/tests/swoole_curl/ssl/version.phpt index 5647a6a0bb3..4c838ef74be 100644 --- a/tests/swoole_curl/ssl/version.phpt +++ b/tests/swoole_curl/ssl/version.phpt @@ -10,8 +10,9 @@ require __DIR__ . '/../../include/bootstrap.php'; $cm = new \SwooleTest\CurlManager(); $cm->run(function ($host) { + $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0'; $ch = curl_init(); - + curl_setopt($ch, CURLOPT_USERAGENT, $userAgent); curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); From 6fef1d4fc0e9747a61a1aba89333dd7af68a3654 Mon Sep 17 00:00:00 2001 From: hantianfeng Date: Thu, 22 Aug 2024 10:33:28 +0800 Subject: [PATCH 09/52] sync github workflows config --- .github/WORKFLOW-PARAMETERS.md | 14 ++++++++++ .github/workflows/{lib.yml => core.yml} | 10 +++++-- .github/workflows/ext.yml | 10 +++---- .github/workflows/framework.yml | 4 +-- .github/workflows/mirror.yml | 28 ------------------- .../workflows/{test-linux.yml => unit.yml} | 7 +++-- .../{conflict-exts.yml => xdebug.yml} | 3 +- core-tests/run.sh | 12 ++++++-- 8 files changed, 46 insertions(+), 42 deletions(-) create mode 100644 .github/WORKFLOW-PARAMETERS.md rename .github/workflows/{lib.yml => core.yml} (80%) delete mode 100644 .github/workflows/mirror.yml rename .github/workflows/{test-linux.yml => unit.yml} (76%) rename .github/workflows/{conflict-exts.yml => xdebug.yml} (82%) 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 80% rename from .github/workflows/lib.yml rename to .github/workflows/core.yml index 03a290a6456..2bd04f285e0 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" @@ -37,6 +37,12 @@ jobs: 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 f79bc4247e5..6806851b32d 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -1,13 +1,13 @@ -name: ext-swoole +name: Compile Tests -on: [push, pull_request] +on: [ push, pull_request ] env: CPPFLAGS: "-I/opt/homebrew/opt/pcre2/include/" 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 9c1ee641498..58e33911c3e 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: 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 diff --git a/.github/workflows/test-linux.yml b/.github/workflows/unit.yml similarity index 76% rename from .github/workflows/test-linux.yml rename to .github/workflows/unit.yml index 85cdd5ab65e..eccdbae5bbb 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/unit.yml @@ -1,10 +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, '--filter=') || contains(github.event.head_commit.message, '[unit]')" strategy: fail-fast: false matrix: @@ -31,7 +32,9 @@ 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 .) + export PHP_VERSION=${{ matrix.php }} ${{runner.workspace}}/swoole-src/scripts/route.sh 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 7ddae7bbcaf..cc9dc635ae6 100644 --- a/.github/workflows/conflict-exts.yml +++ b/.github/workflows/xdebug.yml @@ -1,9 +1,10 @@ -name: conflict-exts +name: Xdebug Support Tests on: [push, pull_request] jobs: tests: + 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 From d14d6a2f73636a08530d364e4d3a55b13922dcdf Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 23 Aug 2024 09:56:14 +0800 Subject: [PATCH 10/52] v5.1.4 (#5458) --- CMakeLists.txt | 2 +- config.m4 | 10 --------- include/swoole_version.h | 2 +- package.xml | 45 ++++++++++++++++++---------------------- 4 files changed, 22 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a03f7f8d82..34c01d81667 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.4-dev) +set(SWOOLE_VERSION 5.1.4) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/config.m4 b/config.m4 index 7587c57cceb..1c59606ec42 100644 --- a/config.m4 +++ b/config.m4 @@ -842,7 +842,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 @@ -977,15 +976,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(brotli, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") - PHP_ADD_LIBRARY_WITH_PATH(brotlienc, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") - PHP_ADD_LIBRARY_WITH_PATH(brotlidec, "${PHP_BROTLI_DIR}/${PHP_LIBDIR}") - fi - if test "$PHP_NGHTTP2_DIR" != "no"; then PHP_ADD_INCLUDE("${PHP_NGHTTP2_DIR}/include") PHP_ADD_LIBRARY_WITH_PATH(nghttp2, "${PHP_NGHTTP2_DIR}/${PHP_LIBDIR}") diff --git a/include/swoole_version.h b/include/swoole_version.h index ddfce84379f..0d3f5dccd20 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -22,7 +22,7 @@ #define SWOOLE_MINOR_VERSION 1 #define SWOOLE_RELEASE_VERSION 4 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.4-dev" +#define SWOOLE_VERSION "5.1.4" #define SWOOLE_VERSION_ID 50104 #define SWOOLE_API_VERSION_ID 0x202208a diff --git a/package.xml b/package.xml index dd40b1e7ec0..ab07241349d 100644 --- a/package.xml +++ b/package.xml @@ -51,10 +51,10 @@ doubaokun@php.net yes - 2024-05-24 - + 2024-08-22 + - 5.1.3 + 5.1.4 5.0 @@ -63,26 +63,11 @@ Apache2.0 - - Fix the problem of being unable to install through pecl. - - Fix the issue of Swoole\Coroutine\FastCGI\Client client being unable to set keepalive. - - Fix the issue of process continuously restarting due to error thrown when request parameters exceed max_input_vars. - - Fix the unknown issue caused by using Swoole\Event::wait() in a coroutine. - - Fix the issue of proc_open not supporting pty when used in a coroutine. - - Fix the segmentation fault issue in pdo_sqlite on PHP 8.3. - - Fix the unnecessary warning when compiling Swoole. - - Fix the error thrown when calling zend_fetch_resource2_ex on closed STDOUT/STDERR. - - Fix the invalid set_tcp_nodelay configuration. - - Fix the occasional unreachable branch issue triggered during file uploads. - - Fix the issue causing PHP core to throw errors when dispatch_func is set. - - Fix the obsolete warning of AC_PROG_CC_C99 in autoconf >= 2.70 version. - - Remove unnecessary checks for socket structs. - - Upgrade the Swoole library. - - Add support for http status code 451 in Swoole\Http\Response. - - Synchronize file operation code across different versions of PHP. - - Synchronize PDO operation code across different versions of PHP. - - Optimize the code for Socket::ssl_recv() function. - - Optimized config.m4 by allowing some configurations to set dependency library locations using pkg-config. - - Optimize the issue with using dynamic arrays when parsing request headers. + - Fix broken build with GCC 14. @remicollet + - Fix could not send SSL negotiation packet(Resource temporarily unavailable). @NathanFreeman + - Fix the issue where certain critical parameters of `Swoole\Server` are not reset to 0 during process restart. @NathanFreeman + - Fix the problem where `Swoole\Http\Request::getMethod()` returns the incorrect request method when `HTTP2` is enabled. @matyhtf + - Optimize `Swoole\Http\Response::end()`. Response data larger than 16K will be sent directly through the socket, while data smaller than or equal to 16K will be copied to the buffer first before being sent out via the socket. @NathanFreeman @@ -1316,6 +1301,7 @@ + @@ -1349,6 +1335,7 @@ + @@ -2537,8 +2524,16 @@ - - + + + + + + + + + + From 5947c35af6a1c950d902994ebe5e0251da1b9465 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 9 Sep 2024 17:22:01 +0800 Subject: [PATCH 11/52] cURL compatible with PHP 8.4 (#5466) --- config.m4 | 3 + ext-src/php_swoole_curl.h | 6 + ext-src/swoole_runtime.cc | 4 + thirdparty/php/curl/curl_arginfo.h | 8 +- 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 + 15 files changed, 3898 insertions(+), 12 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 1c59606ec42..276bc32512b 100644 --- a/config.m4 +++ b/config.m4 @@ -1014,6 +1014,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 \ @@ -1232,6 +1234,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..2dd3b666c28 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -24,7 +24,13 @@ SW_EXTERN_C_BEGIN #include #include +#ifdef SW_USE_CURL +#if PHP_VERSION_ID >= 80400 +#include "thirdparty/php84/curl/curl_private.h" +#else #include "thirdparty/php/curl/curl_private.h" +#endif +#endif SW_EXTERN_C_END #if LIBCURL_VERSION_NUM < 0x073800 diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index a7856f401b4..6c38efd42c0 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -20,8 +20,12 @@ #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..8b259fc9fe5 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) @@ -32,12 +32,6 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_swoole_native_curl_exec, 0, 1, M ZEND_ARG_OBJ_INFO(0, handle, CurlHandle, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_OBJ_INFO_EX(arginfo_swoole_native_curl_file_create, 0, 1, CURLFile, 0) - 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_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") 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 97d5d540f0063510aa5a60142b6b583691cc1128 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 9 Sep 2024 19:22:40 +0800 Subject: [PATCH 12/52] Compatible with PHP 8.4 - 3 (#5467) --- config.m4 | 22 +- ext-src/php_swoole_odbc.h | 10 +- ext-src/php_swoole_oracle.h | 11 +- ext-src/php_swoole_private.h | 4 + ext-src/php_swoole_sqlite.h | 11 +- thirdparty/php80/pdo_oci/php_pdo_oci_int.h | 106 +- 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 +++++ 34 files changed, 8706 insertions(+), 189 deletions(-) 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 276bc32512b..2658b9aa26d 100644 --- a/config.m4 +++ b/config.m4 @@ -1053,7 +1053,10 @@ 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/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 @@ -1063,7 +1066,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 @@ -1073,7 +1078,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 @@ -1083,7 +1090,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/" @@ -1242,20 +1252,24 @@ EOF 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) + 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/php80/pdo_odbc) 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/php80/pdo_oci) 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/php80/pdo_sqlite) 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 8c4f6d989c0..59e19c0f57b 100644 --- a/ext-src/php_swoole_odbc.h +++ b/ext-src/php_swoole_odbc.h @@ -24,10 +24,16 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" -#if PHP_VERSION_ID > 80100 +#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/php80/pdo_odbc/php_pdo_odbc_int.h" +#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 a2f0a3212e7..7ce78a99e3f 100644 --- a/ext-src/php_swoole_oracle.h +++ b/ext-src/php_swoole_oracle.h @@ -25,10 +25,17 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" -#if PHP_VERSION_ID >= 80100 + +#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/php80/pdo_oci/php_pdo_oci_int.h" +#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_private.h b/ext-src/php_swoole_private.h index ed861839396..5b765fb5894 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -182,6 +182,10 @@ extern zend_object_handlers swoole_exception_handlers; extern zend_class_entry *swoole_error_ce; extern zend_class_entry *swoole_resolve_context_ce; extern zend_object_handlers swoole_resolve_context_handlers; +#if PHP_VERSION_ID >= 80400 +extern zend_class_entry *swoole_exit_exception_ce; +extern zend_object_handlers swoole_exit_exception_handlers; +#endif PHP_FUNCTION(swoole_clear_dns_cache); PHP_FUNCTION(swoole_last_error); diff --git a/ext-src/php_swoole_sqlite.h b/ext-src/php_swoole_sqlite.h index ca942270c4b..209a4ad5565 100644 --- a/ext-src/php_swoole_sqlite.h +++ b/ext-src/php_swoole_sqlite.h @@ -24,10 +24,17 @@ BEGIN_EXTERN_C() #include "ext/pdo/php_pdo_driver.h" -#if PHP_VERSION_ID >= 80100 + +#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/php80/pdo_sqlite/php_pdo_sqlite_int.h" +#include "thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h" #endif extern const pdo_driver_t swoole_pdo_sqlite_driver; diff --git a/thirdparty/php80/pdo_oci/php_pdo_oci_int.h b/thirdparty/php80/pdo_oci/php_pdo_oci_int.h index b8d671b2ad6..1aa178f8a64 100644 --- a/thirdparty/php80/pdo_oci/php_pdo_oci_int.h +++ b/thirdparty/php80/pdo_oci/php_pdo_oci_int.h @@ -17,79 +17,78 @@ #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; 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; +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 @@ -97,11 +96,10 @@ extern const struct pdo_stmt_methods oci_stmt_methods; /* 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/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 f0ca8d0fe77608127c54bf046c4810dd2ede8e7b Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 12 Sep 2024 13:21:52 +0800 Subject: [PATCH 13/52] Compatible with PHP 8.4 - 1 (#5465) * Compatible with PHP 8.4 - 1 * fix error * fix error --- .github/workflows/framework.yml | 2 +- .github/workflows/unit.yml | 2 +- .github/workflows/xdebug.yml | 2 +- CMakeLists.txt | 2 +- ext-src/php_swoole_private.h | 4 -- ext-src/swoole_coroutine.cc | 69 +++++++++++++++++++------- ext-src/swoole_mysql_coro.cc | 1 + ext-src/swoole_runtime.cc | 6 +++ include/swoole_version.h | 6 +-- tests/swoole_http_server/exit.phpt | 59 ++++++++++++++++++++++ thirdparty/php/streams/plain_wrapper.c | 11 ++++ 11 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 tests/swoole_http_server/exit.phpt diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index 58e33911c3e..c9ae3953955 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1', '8.2', '8.3' ] + php-version: [ '8.0', '8.1', '8.2', '8.3', '8.4' ] framework: [ 'Laravel Octane', 'Hyperf', 'Simps', 'imi' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index eccdbae5bbb..55f4b64c389 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2', '8.3'] + php: ['8.0', '8.1', '8.2', '8.3', '8.4-dev'] steps: - uses: actions/checkout@v4 - name: Setup PHP diff --git a/.github/workflows/xdebug.yml b/.github/workflows/xdebug.yml index cc9dc635ae6..02dcb58cbe6 100644 --- a/.github/workflows/xdebug.yml +++ b/.github/workflows/xdebug.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2', '8.3'] + php: ['8.0', '8.1', '8.2', '8.3', '8.4-dev'] name: PHP ${{ matrix.php }} - Swoole diff --git a/CMakeLists.txt b/CMakeLists.txt index 34c01d81667..4ccedd00e37 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ PROJECT(libswoole) ENABLE_LANGUAGE(ASM) -set(SWOOLE_VERSION 5.1.4) +set(SWOOLE_VERSION 5.1.5-dev) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/ext-src/php_swoole_private.h b/ext-src/php_swoole_private.h index 5b765fb5894..ed861839396 100644 --- a/ext-src/php_swoole_private.h +++ b/ext-src/php_swoole_private.h @@ -182,10 +182,6 @@ extern zend_object_handlers swoole_exception_handlers; extern zend_class_entry *swoole_error_ce; extern zend_class_entry *swoole_resolve_context_ce; extern zend_object_handlers swoole_resolve_context_handlers; -#if PHP_VERSION_ID >= 80400 -extern zend_class_entry *swoole_exit_exception_ce; -extern zend_object_handlers swoole_exit_exception_handlers; -#endif PHP_FUNCTION(swoole_clear_dns_cache); PHP_FUNCTION(swoole_last_error); diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index e0e9d27799b..c63876926ce 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -73,8 +73,11 @@ 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 zend_atomic_bool *zend_vm_interrupt = nullptr; 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; @@ -180,6 +183,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; @@ -224,6 +228,38 @@ 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(); @@ -858,32 +894,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 +925,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 +944,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); @@ -989,8 +1022,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_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index c388de802b1..bcfd41a0c61 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -26,6 +26,7 @@ #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" diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index 6c38efd42c0..d7a409ab547 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -80,6 +80,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); @@ -237,6 +240,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() { diff --git a/include/swoole_version.h b/include/swoole_version.h index 0d3f5dccd20..c28b616dac1 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 4 +#define SWOOLE_RELEASE_VERSION 5 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.4" -#define SWOOLE_VERSION_ID 50104 +#define SWOOLE_VERSION "5.1.5-dev" +#define SWOOLE_VERSION_ID 50105 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ diff --git a/tests/swoole_http_server/exit.phpt b/tests/swoole_http_server/exit.phpt new file mode 100644 index 00000000000..71fd50a1285 --- /dev/null +++ b/tests/swoole_http_server/exit.phpt @@ -0,0 +1,59 @@ +--TEST-- +swoole_http_server: exit function +--SKIPIF-- + +--FILE-- +parentFunc = function () use ($pm) { + Co\run(function () use ($pm) { + httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); + }); + + $pm->kill(); + echo "DONE\n"; +}; + +$pm->childFunc = function () use ($pm) { + $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); + $http->on('workerStart', function () use ($pm) { + $pm->wakeup(); + }); + + $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { + try { + exit('Swoole Server exit!!!'); + } catch (Exception $e) { + echo $e->getMessage().PHP_EOL; + } + + try { + exit(); + } catch (Exception $e) { + echo $e->getMessage().PHP_EOL; + } + + try { + exit(400); + } catch (Exception $e) { + echo $e->getStatus().PHP_EOL; + } + $response->end(); + }); + $http->start(); +}; +$pm->childFirst(); +$pm->run(); +?> +--EXPECTF-- +Swoole Server exit!!! +swoole exit +400 +DONE 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 964b97f6193790f8b1f002fac73867f48315acf5 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:56:40 +0800 Subject: [PATCH 14/52] [5.1] Fix some functions that have been replaced (#5480) * The php_strtoupper(), php_string_toupper(), php_strtolower(), and php_string_tolower() functions has been removed, use zend_str_toupper(), zend_string_toupper(), zend_str_tolower(), and zend_string_tolower() respectively instead, Replace zend_atol with zend_ini_parse_quantity. * exclude versions lower than PHP 8.2 --- ext-src/php_swoole.cc | 11 ++++++++ ext-src/swoole_coroutine.cc | 2 ++ ext-src/swoole_redis_server.cc | 12 ++++++++ tests/swoole_server/parse_option_to_size.phpt | 28 +++++++++++++++++++ 4 files changed, 53 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 dc6cb10159d..cbb0b2adb30 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -340,7 +340,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/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index c63876926ce..c35e85242d5 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -229,6 +229,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_string *message = NULL; @@ -259,6 +260,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 edb8be271e9..01d20d3aa96 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()) { @@ -214,7 +218,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); @@ -240,7 +248,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/tests/swoole_server/parse_option_to_size.phpt b/tests/swoole_server/parse_option_to_size.phpt new file mode 100644 index 00000000000..25700d05cae --- /dev/null +++ b/tests/swoole_server/parse_option_to_size.phpt @@ -0,0 +1,28 @@ +--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 249fcd06bd281ef450866bb7c84aa34e941084d0 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:49:42 +0800 Subject: [PATCH 15/52] Fix pgsql non blocking error (#5486) --- ext-src/swoole_oracle.cc | 11 ++++++----- ext-src/swoole_pgsql.cc | 9 +++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/ext-src/swoole_oracle.cc b/ext-src/swoole_oracle.cc index af7d90e4e92..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; @@ -49,7 +47,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 +63,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 +80,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 b8c08903a58..260f08d708d 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -28,6 +28,7 @@ #include "thirdparty/php80/pdo_pgsql/php_pdo_pgsql_int.h" #endif +using swoole::Coroutine; using swoole::Reactor; using swoole::coroutine::Socket; using swoole::coroutine::translate_events_to_poll; @@ -42,7 +43,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; @@ -97,7 +98,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 20add68d7d66596841a85a3d00e170cabf2dd553 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 23 Sep 2024 13:36:37 +0800 Subject: [PATCH 16/52] 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 50bce294e30..002a793da29 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 620c7aca035a88bff43623d8e826686532a808cf Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 23 Sep 2024 15:14:51 +0800 Subject: [PATCH 17/52] [5.1] Fix pgsql header version error (#5484) * fix pgsql header version error * remove php8.4 --- .github/workflows/framework.yml | 2 +- .github/workflows/unit.yml | 2 +- .github/workflows/xdebug.yml | 2 +- ext-src/php_swoole_pgsql.h | 12 ++++++++++++ ext-src/swoole_pgsql.cc | 5 ----- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/framework.yml b/.github/workflows/framework.yml index c9ae3953955..58e33911c3e 100644 --- a/.github/workflows/framework.yml +++ b/.github/workflows/framework.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - php-version: [ '8.0', '8.1', '8.2', '8.3', '8.4' ] + php-version: [ '8.0', '8.1', '8.2', '8.3' ] framework: [ 'Laravel Octane', 'Hyperf', 'Simps', 'imi' ] name: ${{ matrix.framework }} - PHP ${{ matrix.php-version }} steps: diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index 55f4b64c389..eccdbae5bbb 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2', '8.3', '8.4-dev'] + php: ['8.0', '8.1', '8.2', '8.3'] steps: - uses: actions/checkout@v4 - name: Setup PHP diff --git a/.github/workflows/xdebug.yml b/.github/workflows/xdebug.yml index 02dcb58cbe6..cc9dc635ae6 100644 --- a/.github/workflows/xdebug.yml +++ b/.github/workflows/xdebug.yml @@ -9,7 +9,7 @@ jobs: strategy: fail-fast: false matrix: - php: ['8.0', '8.1', '8.2', '8.3', '8.4-dev'] + php: ['8.0', '8.1', '8.2', '8.3'] name: PHP ${{ matrix.php }} - Swoole diff --git a/ext-src/php_swoole_pgsql.h b/ext-src/php_swoole_pgsql.h index cdd267f020b..bfebf559883 100644 --- a/ext-src/php_swoole_pgsql.h +++ b/ext-src/php_swoole_pgsql.h @@ -25,6 +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_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/swoole_pgsql.cc b/ext-src/swoole_pgsql.cc index 260f08d708d..6c76f71f70e 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -22,11 +22,6 @@ #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::Coroutine; using swoole::Reactor; From 60bba474dea9aa449254ddbb99d4195b390b75a9 Mon Sep 17 00:00:00 2001 From: "Tianfeng.Han" Date: Tue, 24 Sep 2024 10:04:26 +0800 Subject: [PATCH 18/52] merge master --- include/swoole_static_handler.h | 29 ++++++ src/server/static_handler.cc | 89 +++++++++---------- .../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, 242 insertions(+), 46 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 71d2e8711ca..9626932a0e7 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(); } @@ -128,6 +137,26 @@ 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); + } + + 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() { return content_length; } diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index 002a793da29..a67e51fd9ef 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) { @@ -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,42 +143,30 @@ 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; + // The file does not exist + if (lstat(filename, &file_stat) < 0) { + return catch_error(); } - char real_path[PATH_MAX]; - if (!realpath(filename, real_path)) { - if (last) { - status_code = SW_HTTP_NOT_FOUND; - return true; - } else { - 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(); + } } - } - - 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: - if (lstat(filename, &file_stat) < 0) { - if (last) { - status_code = SW_HTTP_NOT_FOUND; - return true; - } else { - return false; + } else { + if (!get_absolute_path() || !is_located_in_document_root()) { + return catch_error(); } } - 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 +185,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 +270,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; } @@ -327,7 +326,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 +363,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/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 1debe0cf238454bd6ce8e9e3de6d94f892149add Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 24 Sep 2024 10:21:41 +0800 Subject: [PATCH 19/52] fix merge master --- include/swoole_static_handler.h | 2 ++ src/server/static_handler.cc | 11 ----------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/include/swoole_static_handler.h b/include/swoole_static_handler.h index 9626932a0e7..c35d7bf78dc 100644 --- a/include/swoole_static_handler.h +++ b/include/swoole_static_handler.h @@ -125,6 +125,8 @@ class StaticHandler { return std::string(filename, l_filename); } + bool get_absolute_path(); + size_t get_filesize() { return file_stat.st_size; } diff --git a/src/server/static_handler.cc b/src/server/static_handler.cc index a67e51fd9ef..e0015bfad23 100644 --- a/src/server/static_handler.cc +++ b/src/server/static_handler.cc @@ -166,17 +166,6 @@ bool StaticHandler::hit() { } } - if (is_link()) { - char buf[PATH_MAX]; - ssize_t byte = ::readlink(filename, buf, sizeof(buf) - 1); - if (byte <= 0) { - return false; - } - buf[byte] = 0; - swoole_strlcpy(filename, buf, sizeof(filename)); - goto check_stat; - } - if (serv->http_index_files && !serv->http_index_files->empty() && is_dir()) { return true; } From 781bc209be9c7f0231dadd40e3bf23d07d9c8672 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 24 Sep 2024 10:34:02 +0800 Subject: [PATCH 20/52] fix tests, --filter=unit --- tests/swoole_event/add_after_server_start.phpt | 8 ++++---- .../connect_twice.phpt | 18 +++++++++++------- tests/swoole_http_client_coro/http_proxy.phpt | 4 ++-- 3 files changed, 17 insertions(+), 13 deletions(-) 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_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"; }); ?> From 0a3ec9ee3403f70e703f0bddbbd5b0cf384b583c Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 14 Oct 2024 17:32:52 +0800 Subject: [PATCH 21/52] Fixed #5500 --- ext-src/swoole_server.cc | 2 + include/swoole_atomic.h | 1 + include/swoole_server.h | 1 + src/server/manager.cc | 8 ++ src/server/master.cc | 25 +++++ .../reset_concurrency_with_base.phpt | 90 +++++++++++++++++ .../reset_concurrency_with_process.phpt | 97 +++++++++++++++++++ 7 files changed, 224 insertions(+) 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 879a6ac1793..a3243c73bb7 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -2813,6 +2813,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); @@ -2821,6 +2822,7 @@ static PHP_METHOD(swoole_server, stats) { 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); + add_assoc_long_ex(return_value, ZEND_STRL("worker_concurrency"), SwooleWG.worker->concurrency); } if (serv->task_ipc_mode > Server::TASK_IPC_UNIXSOCK && serv->gs->task_workers.queue) { diff --git a/include/swoole_atomic.h b/include/swoole_atomic.h index adead3cd1f2..8b7b1b50008 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_server.h b/include/swoole_server.h index 0ef0f43fff2..2e93c1eb6de 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -933,6 +933,7 @@ 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); int connection_incoming(Reactor *reactor, Connection *conn); int get_idle_worker_num(); diff --git a/src/server/manager.cc b/src/server/manager.cc index efe33394a68..012450ab614 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -169,6 +169,14 @@ void Server::check_worker_exit_status(Worker *worker, const ExitStatus &exit_sta if (onWorkerError != nullptr) { onWorkerError(this, 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. + */ + if (worker->type == SW_PROCESS_WORKER) { + abort_worker(worker); + } } } diff --git a/src/server/master.cc b/src/server/master.cc index 69f09e498a9..dc0aed82199 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1895,6 +1895,31 @@ 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; + + 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) { + gs->concurrency = 0; + } + } + worker->request_count = 0; + worker->response_count = 0; + worker->dispatch_count = 0; + + 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 */ 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 From 10fdccac04ac7e5809203b6dd206b05d8db2332d Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 14 Oct 2024 17:38:50 +0800 Subject: [PATCH 22/52] Eliminate dead code. --- src/server/manager.cc | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/server/manager.cc b/src/server/manager.cc index 012450ab614..88e8f3f7cc3 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -616,17 +616,6 @@ pid_t Server::spawn_event_worker(Worker *worker) { return pid; } - // see https://github.com/swoole/swoole-src/issues/5407 - if (worker->concurrency > 0 && worker_num > 1) { - sw_atomic_sub_fetch(&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; - if (is_base_mode()) { gs->connection_nums[worker->id] = 0; gs->event_workers.main_loop(&gs->event_workers, worker); From e4c86bf9cad60de7dac7c1993087793e30a1db77 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Tue, 15 Oct 2024 19:30:04 +0800 Subject: [PATCH 23/52] Optimize Server::reset_worker_counter() --- ext-src/swoole_http_server.cc | 24 +++++++++++++++++------- include/swoole_server.h | 1 + src/reactor/base.cc | 8 ++++---- src/server/master.cc | 13 ++++++++----- src/server/task_worker.cc | 1 + src/server/worker.cc | 5 +++++ 6 files changed, 36 insertions(+), 16 deletions(-) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index bca5454c9d7..62fe44fbfaf 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; - SwooleWG.worker->concurrency++; - sw_atomic_add_fetch(&serv->gs->concurrency, 1); + if (!sw_server() || !SwooleWG.worker || SwooleWG.shutdown) { + return false; + } + + auto worker = SwooleWG.worker; swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); - if (SwooleWG.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,13 +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; - SwooleWG.worker->concurrency--; - sw_atomic_sub_fetch(&serv->gs->concurrency, 1); + if (!sw_server() || !SwooleWG.worker || SwooleWG.shutdown) { + return; + } + + auto worker = SwooleWG.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", SwooleWG.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/include/swoole_server.h b/include/swoole_server.h index 2e93c1eb6de..2aaa9f6a842 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -934,6 +934,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 dc0aed82199..887e63656c7 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -1895,11 +1895,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) { @@ -1909,8 +1908,12 @@ void Server::abort_worker(Worker *worker) { worker->request_count = 0; worker->response_count = 0; worker->dispatch_count = 0; +} - if (!is_process_mode()) { +void Server::abort_worker(Worker *worker) { + reset_worker_counter(worker); + + if (is_base_mode()) { SW_LOOP_N(SW_SESSION_LIST_SIZE) { Session *session = get_session(i); if (session->reactor_id == worker->id) { diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 6923e3a832f..19b0d81a226 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -204,6 +204,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 f75cb1c6b25..5b9918580fb 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -305,6 +305,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_WORKER) { + reset_worker_counter(worker); + } + /** * force to end. */ From 39a00bf47a26598f1bc7d9091295a2b323b9fdcd Mon Sep 17 00:00:00 2001 From: matyhtf Date: Wed, 16 Oct 2024 10:44:26 +0800 Subject: [PATCH 24/52] Optimize connection_num in SWOOLE_BASE mode --- src/server/manager.cc | 1 - src/server/reactor_process.cc | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/manager.cc b/src/server/manager.cc index 88e8f3f7cc3..16c9d7680e9 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -617,7 +617,6 @@ pid_t Server::spawn_event_worker(Worker *worker) { } if (is_base_mode()) { - gs->connection_nums[worker->id] = 0; gs->event_workers.main_loop(&gs->event_workers, worker); } else { start_event_worker(worker); diff --git a/src/server/reactor_process.cc b/src/server/reactor_process.cc index 446e03f032f..fddd4821831 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -208,6 +208,8 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { Server::worker_signal_init(); + serv->gs->connection_nums[worker->id] = 0; + for (auto ls : serv->ports) { #ifdef HAVE_REUSEPORT if (ls->is_stream() && serv->enable_reuse_port) { @@ -217,6 +219,7 @@ static int ReactorProcess_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; } From 1c0ee3b4102a9c4c67230d058897217e066a5010 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 11:34:04 +0800 Subject: [PATCH 25/52] In the base mode, all connections are closed during the PHP RSHUTDOWN phase. --- ext-src/swoole_http_server.cc | 8 ++++---- ext-src/swoole_server.cc | 3 +++ include/swoole_process_pool.h | 4 ++++ include/swoole_server.h | 5 +++++ src/server/worker.cc | 10 ++++++++++ 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 62fe44fbfaf..26eeb688b6c 100644 --- a/ext-src/swoole_http_server.cc +++ b/ext-src/swoole_http_server.cc @@ -394,11 +394,11 @@ 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() || !SwooleWG.worker || SwooleWG.shutdown) { + if (!sw_server() || !sw_worker() || SwooleWG.shutdown) { return false; } - auto worker = SwooleWG.worker; + auto worker = sw_worker(); swoole_trace("serv->gs->concurrency=%u, max_concurrency=%u", serv->gs->concurrency, serv->gs->max_concurrency); sw_atomic_add_fetch(&serv->gs->concurrency, 1); worker->concurrency++; @@ -417,11 +417,11 @@ 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() || !SwooleWG.worker || SwooleWG.shutdown) { + if (!sw_server() || !sw_worker() || SwooleWG.shutdown) { return; } - auto worker = SwooleWG.worker; + 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--; diff --git a/ext-src/swoole_server.cc b/ext-src/swoole_server.cc index a3243c73bb7..fb71f6aff82 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -99,6 +99,9 @@ void php_swoole_server_rshutdown() { serv->drain_worker_pipe(); if (serv->is_started() && !serv->is_user_worker()) { + if (serv->is_event_worker()) { + serv->clean_worker_connections(sw_worker()); + } if (php_swoole_is_fatal_error()) { swoole_error_log(SW_LOG_ERROR, SW_ERROR_PHP_FATAL_ERROR, diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index cf6fc72f289..f8ec8d25cc1 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -327,3 +327,7 @@ static sw_inline int swoole_kill(pid_t __pid, int __sig) { extern swoole::WorkerGlobal SwooleWG; // Worker Global Variable typedef swoole::ProtocolType swProtocolType; + +static inline swoole::Worker *sw_worker() { + return SwooleWG.worker; +} diff --git a/include/swoole_server.h b/include/swoole_server.h index 2aaa9f6a842..c712f27adf1 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -1095,6 +1095,10 @@ class Server { return SwooleG.process_type == SW_PROCESS_WORKER; } + bool is_event_worker() { + return is_worker(); + } + bool is_task_worker() { return SwooleG.process_type == SW_PROCESS_TASKWORKER; } @@ -1355,6 +1359,7 @@ class Server { static void read_worker_message(ProcessPool *pool, EventData *msg); void drain_worker_pipe(); + void clean_worker_connections(Worker *worker); void check_worker_exit_status(Worker *worker, const ExitStatus &exit_status); diff --git a/src/server/worker.cc b/src/server/worker.cc index 5b9918580fb..cf581c7ab25 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -427,6 +427,16 @@ void Server::drain_worker_pipe() { } } +void Server::clean_worker_connections(Worker *worker) { + sw_reactor()->destroyed = true; + + if (is_base_mode()) { + foreach_connection([this](Connection *conn) { close(conn->session_id, true); }); + } else { + return; + } +} + /** * main loop [Worker] */ From e2a144f3e985df5361b931752643fc7e424d34c2 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 14:11:19 +0800 Subject: [PATCH 26/52] =?UTF-8?q?Optimize=20server=20code=EF=BC=8C=20fix?= =?UTF-8?q?=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 5 +-- core-tests/CMakeLists.txt | 8 ++--- ext-src/swoole_http_server.cc | 4 +-- ext-src/swoole_server.cc | 4 +-- include/swoole_process_pool.h | 15 ++++++++ include/swoole_server.h | 11 +++++- src/os/process_pool.cc | 9 +++++ src/server/master.cc | 16 --------- src/server/worker.cc | 66 ++++++++++++++++++++++++----------- 9 files changed, 90 insertions(+), 48 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4ccedd00e37..9e55521b8e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,5 @@ PROJECT(libswoole) +cmake_minimum_required(VERSION 2.8.12) ENABLE_LANGUAGE(ASM) set(SWOOLE_VERSION 5.1.5-dev) @@ -6,7 +7,6 @@ set(SWOOLE_VERSION 5.1.5-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) @@ -122,7 +122,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) 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/ext-src/swoole_http_server.cc b/ext-src/swoole_http_server.cc index 26eeb688b6c..b60723e1e6c 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 fb71f6aff82..52f425a567d 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -99,6 +99,7 @@ void php_swoole_server_rshutdown() { serv->drain_worker_pipe(); if (serv->is_started() && !serv->is_user_worker()) { + sw_worker()->shutdown(); if (serv->is_event_worker()) { serv->clean_worker_connections(sw_worker()); } @@ -1514,10 +1515,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 = (zval *) serv->private_data_2; ServerObject *server_object = server_fetch_object(Z_OBJ_P(zserv)); diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index f8ec8d25cc1..9fff9dac708 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -105,8 +105,20 @@ struct Worker; struct WorkerGlobal { bool run_always; 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; }; @@ -115,6 +127,7 @@ struct Worker { WorkerId id; ProcessPool *pool; MsgQueue *queue; + bool shared; bool redirect_stdout; bool redirect_stdin; @@ -148,6 +161,8 @@ struct Worker { void *ptr2; ssize_t send_pipe_message(const void *buf, size_t n, int flags); + 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 c712f27adf1..1a33f5a0ff6 100644 --- a/include/swoole_server.h +++ b/include/swoole_server.h @@ -795,10 +795,13 @@ 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; @@ -1087,6 +1090,10 @@ class Server { return factory != nullptr; } + bool is_running() { + return running; + } + bool is_master() { return SwooleG.process_type == SW_PROCESS_MASTER; } @@ -1238,6 +1245,8 @@ 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); ResultCode 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 8c6212ea0ae..0a7709006a8 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -986,4 +986,13 @@ void ProcessPool::destroy() { sw_mem_pool()->free(workers); } +void Worker::shutdown() { + status = SW_WORKER_EXIT; + SwooleWG.shutdown = true; +} + +bool Worker::is_shutdown() { + return SwooleWG.shutdown; +} + } // namespace swoole diff --git a/src/server/master.cc b/src/server/master.cc index 887e63656c7..46b0445c320 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -588,22 +588,6 @@ 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() { if (start_check() < 0) { return SW_ERR; diff --git a/src/server/worker.cc b/src/server/worker.cc index cf581c7ab25..1f185a761ee 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -47,18 +47,17 @@ 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() || !sw_server()->is_running()) { return; } switch (signo) { case SIGTERM: - // Event worker if (swoole_event_is_available()) { - sw_server()->stop_async_worker(SwooleWG.worker); - } - // Task worker - else { - SwooleWG.shutdown = true; + // Event Worker + sw_server()->stop_async_worker(sw_worker()); + } else { + // Task Worker + sw_worker()->shutdown(); } break; // for test @@ -261,7 +260,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); @@ -285,27 +284,58 @@ 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); + } + + 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) 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()) { swoole_error_log( SW_LOG_WARNING, SW_ERROR_SERVER_WORKER_UNPROCESSED_DATA, "unprocessed data in the worker process buffer"); message_bus.clear(); } + + SwooleWG.running = false; + if (SwooleWG.worker_copy) { + delete SwooleWG.worker_copy; + SwooleWG.worker_copy = nullptr; + SwooleWG.worker = nullptr; + } } 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_WORKER) { reset_worker_counter(worker); } @@ -314,7 +344,6 @@ void Server::stop_async_worker(Worker *worker) { * force to end. */ if (reload_async == 0) { - running = false; reactor->running = false; return; } @@ -325,8 +354,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) { @@ -359,10 +388,10 @@ 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; + swoole_sys_warning("failed to push WORKER_STOP message"); } } @@ -371,9 +400,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) { @@ -389,10 +415,9 @@ 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, SwooleWG.worker); + serv->onWorkerExit(serv, sw_worker()); call_worker_exit_func = 1; continue; } @@ -401,7 +426,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) { From d9b05bbf89b07317ac1c446fdcf4ff83d48c44bf Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 15:05:15 +0800 Subject: [PATCH 27/52] Fix tests --- ext-src/php_swoole.cc | 4 ++-- ext-src/swoole_coroutine.cc | 8 +++++++- tests/include/api/curl_multi.php | 7 ++----- tests/swoole_server/memory_leak/task.phpt | 7 ++++++- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index cbb0b2adb30..3dab63ba1ab 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -1052,8 +1052,6 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { rshutdown_callbacks.execute(); - swoole_event_free(); - php_swoole_server_rshutdown(); php_swoole_http_server_rshutdown(); php_swoole_async_coro_rshutdown(); @@ -1063,6 +1061,8 @@ PHP_RSHUTDOWN_FUNCTION(swoole) { php_swoole_runtime_rshutdown(); php_swoole_process_rshutdown(); + swoole_event_free(); + SwooleG.running = 0; SWOOLE_G(req_status) = PHP_SWOOLE_RSHUTDOWN_END; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index c35e85242d5..4cf3252a15e 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -423,6 +423,10 @@ void PHPCoroutine::activate() { } void PHPCoroutine::deactivate(void *ptr) { + if (sw_unlikely(!activated)) { + return; + } + activated = false; interrupt_thread_stop(); /** * reset runtime hook @@ -441,10 +445,12 @@ void PHPCoroutine::deactivate(void *ptr) { enable_unsafe_function(); Coroutine::deactivate(); - activated = false; } void PHPCoroutine::shutdown() { + if (activated) { + deactivate(nullptr); + } interrupt_thread_stop(); Coroutine::bailout(nullptr); if (options) { diff --git a/tests/include/api/curl_multi.php b/tests/include/api/curl_multi.php index f146e9fa018..30d457e8d8c 100644 --- a/tests/include/api/curl_multi.php +++ b/tests/include/api/curl_multi.php @@ -8,17 +8,14 @@ function swoole_test_curl_multi($options = []) { function swoole_test_curl_multi_ex($mh, $options = []) { $ch1 = curl_init(); $ch2 = curl_init(); - $userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:129.0) Gecko/20100101 Firefox/129.0'; // 设置URL和相应的选项 curl_setopt($ch1, CURLOPT_URL, "https://www.baidu.com/"); curl_setopt($ch1, CURLOPT_HEADER, 0); - curl_setopt($ch2, CURLOPT_USERAGENT, $userAgent); 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_USERAGENT, $userAgent); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, 1); $mh = curl_multi_init(); @@ -64,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_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 From a96b6498b07c7752dbbff15b44c70daafc20921a Mon Sep 17 00:00:00 2001 From: matyhtf Date: Thu, 17 Oct 2024 15:41:10 +0800 Subject: [PATCH 28/52] Fix tests [2] --- core-tests/src/server/server.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index c66e88a64aa..aec005f4f5b 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -499,7 +499,7 @@ TEST(server, task_worker) { exit(2); } - serv.onTask = [](Server *serv, swEventData *task) -> int { + serv.onTask = [](Server *serv, EventData *task) -> int { EXPECT_EQ(serv->get_task_count(), 1); EXPECT_EQ(string(task->data, task->info.len), string(packet)); serv->gs->task_workers.running = 0; @@ -509,6 +509,10 @@ TEST(server, task_worker) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); + SwooleWG.shutdown = false; + SwooleWG.running = true; + SwooleWG.run_always = true; + thread t1([&serv]() { serv.gs->task_workers.running = 1; serv.gs->tasking_num++; From b28c2ddadf99c4f52d5db2c56ad82a8039335197 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 18 Oct 2024 15:15:49 +0800 Subject: [PATCH 29/52] Optimize code, sync master branch --- include/swoole.h | 10 ++----- include/swoole_process_pool.h | 17 ++++++++++- src/core/base.cc | 12 ++------ src/os/process_pool.cc | 53 ++++++++++++++++++++--------------- src/server/base.cc | 1 - src/server/manager.cc | 2 +- src/server/master.cc | 13 ++------- src/server/reactor_process.cc | 4 --- src/server/task_worker.cc | 17 ++--------- src/server/worker.cc | 2 +- 10 files changed, 59 insertions(+), 72 deletions(-) diff --git a/include/swoole.h b/include/swoole.h index ed1a834a76e..c1e5d6eb096 100644 --- a/include/swoole.h +++ b/include/swoole.h @@ -513,17 +513,13 @@ 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, SW_PROCESS_WORKER = 2, SW_PROCESS_MANAGER = 3, + SW_PROCESS_EVENTWORKER = 2, SW_PROCESS_TASKWORKER = 4, SW_PROCESS_USERWORKER = 5, }; @@ -753,7 +749,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 9fff9dac708..329b584fb18 100644 --- a/include/swoole_process_pool.h +++ b/include/swoole_process_pool.h @@ -161,13 +161,29 @@ struct Worker { void *ptr2; 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 start(); void shutdown(); bool is_shutdown(); + bool is_running(); void set_status(enum swWorkerStatus _status) { 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; } @@ -300,7 +316,6 @@ struct ProcessPool { void set_protocol(enum ProtocolType _protocol_type); void set_max_request(uint32_t _max_request, uint32_t _max_request_grace); - int get_max_request(); bool detach(); int wait(); int start(); diff --git a/src/core/base.cc b/src/core/base.cc index 37b723a6dc6..78d09df331a 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]; static std::unordered_map functions; 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/os/process_pool.cc b/src/os/process_pool.cc index 0a7709006a8..10049a7cd5e 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -494,19 +494,6 @@ 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; @@ -518,12 +505,7 @@ 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; /** * Use from_fd save the task_worker->id @@ -536,7 +518,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 && !worker->is_shutdown() && !worker->has_exceeded_max_request()) { /** * fetch task */ @@ -609,8 +591,8 @@ static int ProcessPool_worker_loop_with_task_protocol(ProcessPool *pool, Worker goto _alarm_handler; } - if (ret >= 0 && !worker_task_always) { - task_n--; + if (ret >= 0) { + worker->add_request_count(); } } return SW_OK; @@ -986,6 +968,30 @@ 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() { + start_time = ::time(nullptr); + request_count = 0; + set_status_to_idle(); + SwooleWG.running = true; + 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; @@ -995,4 +1001,7 @@ bool Worker::is_shutdown() { return SwooleWG.shutdown; } +bool Worker::is_running() { + return SwooleWG.running; +} } // namespace swoole diff --git a/src/server/base.cc b/src/server/base.cc index bdb5ef37e47..eb4349e01d9 100644 --- a/src/server/base.cc +++ b/src/server/base.cc @@ -19,7 +19,6 @@ namespace swoole { bool BaseFactory::start() { - SwooleWG.run_always = true; return true; } diff --git a/src/server/manager.cc b/src/server/manager.cc index 16c9d7680e9..cd2ad745394 100644 --- a/src/server/manager.cc +++ b/src/server/manager.cc @@ -666,7 +666,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/master.cc b/src/server/master.cc index 46b0445c320..15e3b97071b 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -575,17 +575,8 @@ void Server::init_worker(Worker *worker) { } #endif - if (max_request < 1) { - SwooleWG.run_always = true; - } else { - 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; + 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 fddd4821831..cb6703775b7 100644 --- a/src/server/reactor_process.cc +++ b/src/server/reactor_process.cc @@ -185,10 +185,6 @@ static int ReactorProcess_loop(ProcessPool *pool, Worker *worker) { SwooleG.pid = getpid(); 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; diff --git a/src/server/task_worker.cc b/src/server/task_worker.cc index 19b0d81a226..67fe7af7b6f 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -191,20 +191,9 @@ 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->start(); + worker->set_max_request(pool->max_request, pool->max_request_grace); SwooleWG.worker = worker; - SwooleWG.worker->status = SW_WORKER_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; } static void TaskWorker_onStop(ProcessPool *pool, Worker *worker) { @@ -228,7 +217,7 @@ static int TaskWorker_onPipeReceive(Reactor *reactor, Event *event) { worker->status = SW_WORKER_IDLE; worker->request_count++; // 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; diff --git a/src/server/worker.cc b/src/server/worker.cc index 1f185a761ee..5b8aba04f3e 100644 --- a/src/server/worker.cc +++ b/src/server/worker.cc @@ -208,7 +208,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); } } From a89e2dbcdc2f63ac55d79451e6f57992ed6254a8 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:23:57 +0800 Subject: [PATCH 30/52] skip ci (#5523) --- composer.json | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 composer.json diff --git a/composer.json b/composer.json new file mode 100644 index 00000000000..e80595b93ea --- /dev/null +++ b/composer.json @@ -0,0 +1,66 @@ +{ + "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.0" + }, + "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" + } + ] + } +} \ No newline at end of file From 131792f909e8e32f4f0080993038ab5f9e32c763 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Fri, 18 Oct 2024 23:40:02 +0800 Subject: [PATCH 31/52] remove php8.4 support (#5525) --- config.m4 | 25 +- ext-src/php_swoole_curl.h | 6 - ext-src/php_swoole_odbc.h | 4 +- ext-src/php_swoole_oracle.h | 4 +- ext-src/php_swoole_pgsql.h | 4 +- ext-src/php_swoole_sqlite.h | 4 +- ext-src/swoole_coroutine.cc | 41 - ext-src/swoole_redis_server.cc | 12 - ext-src/swoole_runtime.cc | 10 - tests/swoole_http_server/exit.phpt | 59 - 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/php/streams/plain_wrapper.c | 11 - thirdparty/php80/pdo_oci/oci_driver.c | 2 +- thirdparty/php80/pdo_oci/oci_statement.c | 2 +- thirdparty/php80/pdo_odbc/odbc_driver.c | 2 +- thirdparty/php80/pdo_odbc/odbc_stmt.c | 2 +- thirdparty/php80/pdo_pgsql/pgsql_driver.c | 2 +- thirdparty/php80/pdo_pgsql/pgsql_statement.c | 2 +- thirdparty/php80/pdo_sqlite/sqlite_driver.c | 2 +- .../php80/pdo_sqlite/sqlite_statement.c | 2 +- thirdparty/php81/pdo_oci/oci_driver.c | 2 +- thirdparty/php81/pdo_oci/oci_statement.c | 2 +- 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/php81/pdo_sqlite/sqlite_driver.c | 2 +- .../php81/pdo_sqlite/sqlite_statement.c | 2 +- thirdparty/php83/pdo_oci/oci_driver.c | 2 +- thirdparty/php83/pdo_oci/oci_statement.c | 2 +- thirdparty/php83/pdo_odbc/odbc_driver.c | 2 +- thirdparty/php83/pdo_odbc/odbc_stmt.c | 2 +- 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/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 - 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 --- 65 files changed, 38 insertions(+), 12572 deletions(-) delete mode 100644 tests/swoole_http_server/exit.phpt 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 delete mode 100644 thirdparty/php84/pdo_oci/LICENSE delete mode 100644 thirdparty/php84/pdo_oci/oci_driver.c delete mode 100644 thirdparty/php84/pdo_oci/oci_statement.c delete mode 100644 thirdparty/php84/pdo_oci/php_pdo_oci_int.h delete mode 100644 thirdparty/php84/pdo_odbc/odbc_driver.c delete mode 100644 thirdparty/php84/pdo_odbc/odbc_stmt.c delete mode 100644 thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h delete mode 100644 thirdparty/php84/pdo_pgsql/pgsql_driver.c delete mode 100644 thirdparty/php84/pdo_pgsql/pgsql_driver_arginfo.h delete mode 100644 thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c delete mode 100644 thirdparty/php84/pdo_pgsql/pgsql_statement.c delete mode 100644 thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h delete mode 100644 thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h delete mode 100644 thirdparty/php84/pdo_sqlite/sqlite_driver.c delete mode 100644 thirdparty/php84/pdo_sqlite/sqlite_driver.stub.php delete mode 100644 thirdparty/php84/pdo_sqlite/sqlite_driver_arginfo.h delete mode 100644 thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c delete mode 100644 thirdparty/php84/pdo_sqlite/sqlite_statement.c diff --git a/config.m4 b/config.m4 index 2658b9aa26d..1c59606ec42 100644 --- a/config.m4 +++ b/config.m4 @@ -1014,8 +1014,6 @@ 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 \ @@ -1053,10 +1051,7 @@ 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/php84/pdo_pgsql/pgsql_driver.c \ - thirdparty/php84/pdo_pgsql/pgsql_statement.c \ - thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c" + thirdparty/php83/pdo_pgsql/pgsql_statement.c" fi if test "$PHP_SWOOLE_ORACLE" != "no"; then @@ -1066,9 +1061,7 @@ 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/php84/pdo_oci/oci_driver.c \ - thirdparty/php84/pdo_oci/oci_statement.c" + thirdparty/php83/pdo_oci/oci_statement.c" fi if test "$PHP_SWOOLE_ODBC" != "no"; then @@ -1078,9 +1071,7 @@ 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/php84/pdo_odbc/odbc_driver.c \ - thirdparty/php84/pdo_odbc/odbc_stmt.c" + thirdparty/php83/pdo_odbc/odbc_stmt.c" fi if test "$PHP_SWOOLE_SQLITE" != "no"; then @@ -1090,10 +1081,7 @@ 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/php84/pdo_sqlite/sqlite_driver.c \ - thirdparty/php84/pdo_sqlite/sqlite_statement.c \ - thirdparty/php84/pdo_sqlite/sqlite_sql_parser.c" + thirdparty/php83/pdo_sqlite/sqlite_statement.c" fi SW_ASM_DIR="thirdparty/boost/asm/" @@ -1244,7 +1232,6 @@ 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 @@ -1252,24 +1239,20 @@ EOF 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) - 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/php80/pdo_odbc) 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/php80/pdo_oci) 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/php80/pdo_sqlite) 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_curl.h b/ext-src/php_swoole_curl.h index 2dd3b666c28..4977d95b1ae 100644 --- a/ext-src/php_swoole_curl.h +++ b/ext-src/php_swoole_curl.h @@ -24,13 +24,7 @@ SW_EXTERN_C_BEGIN #include #include -#ifdef SW_USE_CURL -#if PHP_VERSION_ID >= 80400 -#include "thirdparty/php84/curl/curl_private.h" -#else #include "thirdparty/php/curl/curl_private.h" -#endif -#endif SW_EXTERN_C_END #if LIBCURL_VERSION_NUM < 0x073800 diff --git a/ext-src/php_swoole_odbc.h b/ext-src/php_swoole_odbc.h index 59e19c0f57b..e20be99b304 100644 --- a/ext-src/php_swoole_odbc.h +++ b/ext-src/php_swoole_odbc.h @@ -30,10 +30,8 @@ BEGIN_EXTERN_C() #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 +#elif PHP_VERSION_ID >= 80300 #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 7ce78a99e3f..43b090d929f 100644 --- a/ext-src/php_swoole_oracle.h +++ b/ext-src/php_swoole_oracle.h @@ -32,10 +32,8 @@ BEGIN_EXTERN_C() #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 +#elif PHP_VERSION_ID >= 80300 #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 bfebf559883..1606f672077 100644 --- a/ext-src/php_swoole_pgsql.h +++ b/ext-src/php_swoole_pgsql.h @@ -31,10 +31,8 @@ BEGIN_EXTERN_C() #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 +#elif PHP_VERSION_ID >= 80300 #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; diff --git a/ext-src/php_swoole_sqlite.h b/ext-src/php_swoole_sqlite.h index 209a4ad5565..e14b4e9b1aa 100644 --- a/ext-src/php_swoole_sqlite.h +++ b/ext-src/php_swoole_sqlite.h @@ -31,10 +31,8 @@ BEGIN_EXTERN_C() #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 +#elif PHP_VERSION_ID >= 80300 #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; diff --git a/ext-src/swoole_coroutine.cc b/ext-src/swoole_coroutine.cc index 4cf3252a15e..d20378f14d3 100644 --- a/ext-src/swoole_coroutine.cc +++ b/ext-src/swoole_coroutine.cc @@ -72,11 +72,7 @@ std::thread PHPCoroutine::interrupt_thread; bool PHPCoroutine::interrupt_thread_running = false; extern void php_swoole_load_library(); - -#if PHP_VERSION_ID < 80400 static user_opcode_handler_t ori_exit_handler = nullptr; -#endif - static zend_atomic_bool *zend_vm_interrupt = nullptr; static user_opcode_handler_t ori_begin_silence_handler = nullptr; static user_opcode_handler_t ori_end_silence_handler = nullptr; @@ -183,7 +179,6 @@ 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; @@ -228,40 +223,6 @@ 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_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); - } -} -SW_EXTERN_C_END -#endif static int coro_begin_silence_handler(zend_execute_data *execute_data) { PHPContext *task = PHPCoroutine::get_context(); @@ -1030,10 +991,8 @@ 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_redis_server.cc b/ext-src/swoole_redis_server.cc index 01d20d3aa96..edb8be271e9 100644 --- a/ext-src/swoole_redis_server.cc +++ b/ext-src/swoole_redis_server.cc @@ -156,11 +156,7 @@ 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()) { @@ -218,11 +214,7 @@ 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); @@ -248,11 +240,7 @@ 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 d7a409ab547..a7856f401b4 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -20,12 +20,8 @@ #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 @@ -80,9 +76,6 @@ 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); @@ -240,9 +233,6 @@ 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() { diff --git a/tests/swoole_http_server/exit.phpt b/tests/swoole_http_server/exit.phpt deleted file mode 100644 index 71fd50a1285..00000000000 --- a/tests/swoole_http_server/exit.phpt +++ /dev/null @@ -1,59 +0,0 @@ ---TEST-- -swoole_http_server: exit function ---SKIPIF-- - ---FILE-- -parentFunc = function () use ($pm) { - Co\run(function () use ($pm) { - httpRequest("http://127.0.0.1:{$pm->getFreePort()}"); - }); - - $pm->kill(); - echo "DONE\n"; -}; - -$pm->childFunc = function () use ($pm) { - $http = new Swoole\Http\Server('127.0.0.1', $pm->getFreePort(), SWOOLE_BASE); - $http->on('workerStart', function () use ($pm) { - $pm->wakeup(); - }); - - $http->on('request', function (Swoole\Http\Request $request, Swoole\Http\Response $response) use ($pm) { - try { - exit('Swoole Server exit!!!'); - } catch (Exception $e) { - echo $e->getMessage().PHP_EOL; - } - - try { - exit(); - } catch (Exception $e) { - echo $e->getMessage().PHP_EOL; - } - - try { - exit(400); - } catch (Exception $e) { - echo $e->getStatus().PHP_EOL; - } - $response->end(); - }); - $http->start(); -}; -$pm->childFirst(); -$pm->run(); -?> ---EXPECTF-- -Swoole Server exit!!! -swoole exit -400 -DONE diff --git a/thirdparty/php/curl/curl_arginfo.h b/thirdparty/php/curl/curl_arginfo.h index 8b259fc9fe5..39e14ba2ce6 100644 --- a/thirdparty/php/curl/curl_arginfo.h +++ b/thirdparty/php/curl/curl_arginfo.h @@ -3,7 +3,7 @@ #include "curl_interface.h" -#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 +#ifdef SW_USE_CURL 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 f65e86f9962..cdfcf44aebd 100644 --- a/thirdparty/php/curl/curl_interface.h +++ b/thirdparty/php/curl/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 diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index c2a393961aa..b8be8932b59 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -17,7 +17,7 @@ /* Copied from PHP-4f68662f5b61aecf90f6d8005976f5f91d4ce8d3 */ -#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 +#ifdef SW_USE_CURL #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 bffc1859465..a70a63ee3ac 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -19,7 +19,7 @@ #include "php_swoole_cxx.h" -#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 +#ifdef SW_USE_CURL #include "php_swoole_curl.h" using namespace swoole; diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index abf997c0b9b..52f6ccb6931 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" -#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 +#ifdef SW_USE_CURL #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 2075384155b..046f6b79223 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 */ -#if defined(SW_USE_CURL) && PHP_VERSION_ID < 80400 +#ifdef SW_USE_CURL #ifndef _PHP_CURL_H #define _PHP_CURL_H diff --git a/thirdparty/php/streams/plain_wrapper.c b/thirdparty/php/streams/plain_wrapper.c index 28c61ef9022..c283f85b930 100644 --- a/thirdparty/php/streams/plain_wrapper.c +++ b/thirdparty/php/streams/plain_wrapper.c @@ -21,23 +21,12 @@ #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 diff --git a/thirdparty/php80/pdo_oci/oci_driver.c b/thirdparty/php80/pdo_oci/oci_driver.c index cba6c1bb865..370401c4b49 100644 --- a/thirdparty/php80/pdo_oci/oci_driver.c +++ b/thirdparty/php80/pdo_oci/oci_driver.c @@ -17,7 +17,7 @@ #define SW_USE_ORACLE_HOOK #include "php_swoole_oracle.h" -#if PHP_VERSION_ID < 80100 +#if defined(SW_USE_ORACLE) && PHP_VERSION_ID < 80100 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php80/pdo_oci/oci_statement.c b/thirdparty/php80/pdo_oci/oci_statement.c index 5e4ae72c12a..12b1ad5e262 100644 --- a/thirdparty/php80/pdo_oci/oci_statement.c +++ b/thirdparty/php80/pdo_oci/oci_statement.c @@ -17,7 +17,7 @@ #define SW_USE_ORACLE_HOOK #include "php_swoole_oracle.h" -#if PHP_VERSION_ID < 80100 +#if defined(SW_USE_ORACLE) && PHP_VERSION_ID < 80100 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php80/pdo_odbc/odbc_driver.c b/thirdparty/php80/pdo_odbc/odbc_driver.c index 2c666cc2104..0e7c7b9722c 100644 --- a/thirdparty/php80/pdo_odbc/odbc_driver.c +++ b/thirdparty/php80/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 defined(SW_USE_ODBC) && PHP_VERSION_ID < 80100 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php80/pdo_odbc/odbc_stmt.c b/thirdparty/php80/pdo_odbc/odbc_stmt.c index 12a1bb203c8..b814cc6b8f5 100644 --- a/thirdparty/php80/pdo_odbc/odbc_stmt.c +++ b/thirdparty/php80/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 defined(SW_USE_ODBC) && PHP_VERSION_ID < 80100 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php80/pdo_pgsql/pgsql_driver.c b/thirdparty/php80/pdo_pgsql/pgsql_driver.c index 34661423741..2474af62045 100644 --- a/thirdparty/php80/pdo_pgsql/pgsql_driver.c +++ b/thirdparty/php80/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 defined(SW_USE_PGSQL) && PHP_VERSION_ID < 80100 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php80/pdo_pgsql/pgsql_statement.c b/thirdparty/php80/pdo_pgsql/pgsql_statement.c index 6bb17aaad4f..4e54cfee27d 100644 --- a/thirdparty/php80/pdo_pgsql/pgsql_statement.c +++ b/thirdparty/php80/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 defined(SW_USE_PGSQL) && PHP_VERSION_ID < 80100 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php80/pdo_sqlite/sqlite_driver.c b/thirdparty/php80/pdo_sqlite/sqlite_driver.c index 21d66d0f7d3..ea9b3dd3b13 100644 --- a/thirdparty/php80/pdo_sqlite/sqlite_driver.c +++ b/thirdparty/php80/pdo_sqlite/sqlite_driver.c @@ -17,7 +17,7 @@ #define SW_USE_SQLITE_HOOK #include "php_swoole_sqlite.h" -#if PHP_VERSION_ID < 80100 +#if defined(SW_USE_SQLITE) && PHP_VERSION_ID < 80100 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php80/pdo_sqlite/sqlite_statement.c b/thirdparty/php80/pdo_sqlite/sqlite_statement.c index ca94200a679..5864d1ec39a 100644 --- a/thirdparty/php80/pdo_sqlite/sqlite_statement.c +++ b/thirdparty/php80/pdo_sqlite/sqlite_statement.c @@ -17,7 +17,7 @@ #define SW_USE_SQLITE_HOOK #include "php_swoole_sqlite.h" -#if PHP_VERSION_ID < 80100 +#if defined(SW_USE_SQLITE) && PHP_VERSION_ID < 80100 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php81/pdo_oci/oci_driver.c b/thirdparty/php81/pdo_oci/oci_driver.c index 8f0207c2eb4..56ea3608911 100644 --- a/thirdparty/php81/pdo_oci/oci_driver.c +++ b/thirdparty/php81/pdo_oci/oci_driver.c @@ -17,7 +17,7 @@ #define SW_USE_ORACLE_HOOK #include "php_swoole_oracle.h" -#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 +#if defined(SW_USE_ORACLE) && PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php81/pdo_oci/oci_statement.c b/thirdparty/php81/pdo_oci/oci_statement.c index c32afea10c5..0f585c65eb6 100644 --- a/thirdparty/php81/pdo_oci/oci_statement.c +++ b/thirdparty/php81/pdo_oci/oci_statement.c @@ -16,7 +16,7 @@ #define SW_USE_ORACLE_HOOK #include "php_swoole_oracle.h" -#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 +#if defined(SW_USE_ORACLE) && PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php81/pdo_odbc/odbc_driver.c b/thirdparty/php81/pdo_odbc/odbc_driver.c index 26bb8e5f682..16a49ce40e2 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 && PHP_VERSION_ID < 80300 +#if defined(SW_USE_ODBC) && 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 cbb0d3cdff1..0daf33fffd8 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 && PHP_VERSION_ID < 80300 +#if defined(SW_USE_ODBC) && 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 c65d29d8bd8..556565add4a 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 && PHP_VERSION_ID < 80300 +#if defined(SW_USE_PGSQL) && 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 f1b958e53f1..263b9398af0 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 && PHP_VERSION_ID < 80300 +#if defined(SW_USE_PGSQL) && PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php81/pdo_sqlite/sqlite_driver.c b/thirdparty/php81/pdo_sqlite/sqlite_driver.c index 61f6948a202..fd262ee45fa 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 && PHP_VERSION_ID < 80300 +#if defined(SW_USE_SQLITE) && 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_sqlite/sqlite_statement.c b/thirdparty/php81/pdo_sqlite/sqlite_statement.c index 9081cbb7788..ec22bee92b6 100644 --- a/thirdparty/php81/pdo_sqlite/sqlite_statement.c +++ b/thirdparty/php81/pdo_sqlite/sqlite_statement.c @@ -17,7 +17,7 @@ #define SW_USE_SQLITE_HOOK #include "php_swoole_sqlite.h" -#if PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 +#if defined(SW_USE_SQLITE) && PHP_VERSION_ID >= 80100 && PHP_VERSION_ID < 80300 #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" diff --git a/thirdparty/php83/pdo_oci/oci_driver.c b/thirdparty/php83/pdo_oci/oci_driver.c index fb6aa6864fe..52f6bc37ec8 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 && PHP_VERSION_ID < 80400 +#if defined(SW_USE_ORACLE) && PHP_VERSION_ID >= 80300 #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 11e8e0c5630..6ce3b4ca765 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 && PHP_VERSION_ID < 80400 +#if defined(SW_USE_ORACLE) && PHP_VERSION_ID >= 80300 #include "php.h" #include "php_ini.h" diff --git a/thirdparty/php83/pdo_odbc/odbc_driver.c b/thirdparty/php83/pdo_odbc/odbc_driver.c index 5c7cbe76890..337d38cb3be 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 && PHP_VERSION_ID < 80400 +#if defined(SW_USE_ODBC) && PHP_VERSION_ID >= 80300 #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 da75f668415..a34570e801d 100644 --- a/thirdparty/php83/pdo_odbc/odbc_stmt.c +++ b/thirdparty/php83/pdo_odbc/odbc_stmt.c @@ -17,7 +17,7 @@ #define SW_USE_ODBC_HOOK #include "php_swoole_odbc.h" -#if PHP_VERSION_ID >= 80300 && PHP_VERSION_ID < 80400 +#if defined(SW_USE_ODBC) && PHP_VERSION_ID >= 80300 #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 86b05d37fc6..a1946c49628 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 && PHP_VERSION_ID < 80400 +#if defined(SW_USE_PGSQL) && PHP_VERSION_ID >= 80300 #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 ae4e7e16fea..0914b50f251 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 && PHP_VERSION_ID < 80400 +#if defined(SW_USE_PGSQL) && PHP_VERSION_ID >= 80300 #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 10f48849e53..cd363714f94 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 && PHP_VERSION_ID < 80400 +#if defined(SW_USE_SQLITE) && PHP_VERSION_ID >= 80300 #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 bf8978f1134..7355b9dd39d 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 && PHP_VERSION_ID < 80400 +#if defined(SW_USE_SQLITE) && PHP_VERSION_ID >= 80300 #include "php.h" #include "php_ini.h" #include "ext/standard/info.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 4890ed1df29..00000000000 --- a/thirdparty/php84/curl/curl_private.h +++ /dev/null @@ -1,167 +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; -#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 4f363eec14e..00000000000 --- a/thirdparty/php84/curl/interface.cc +++ /dev/null @@ -1,2805 +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 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 deleted file mode 100644 index 316d715c328..00000000000 --- a/thirdparty/php84/curl/multi.cc +++ /dev/null @@ -1,636 +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); - - 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 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 diff --git a/thirdparty/php84/pdo_oci/LICENSE b/thirdparty/php84/pdo_oci/LICENSE deleted file mode 100644 index 0815d7eb791..00000000000 --- a/thirdparty/php84/pdo_oci/LICENSE +++ /dev/null @@ -1,68 +0,0 @@ --------------------------------------------------------------------- - 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 deleted file mode 100644 index 6ee73fd7110..00000000000 --- a/thirdparty/php84/pdo_oci/oci_driver.c +++ /dev/null @@ -1,880 +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: 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 deleted file mode 100644 index cd91dbf7223..00000000000 --- a/thirdparty/php84/pdo_oci/oci_statement.c +++ /dev/null @@ -1,1041 +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: 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 deleted file mode 100644 index de3b75d1777..00000000000 --- a/thirdparty/php84/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: | - | 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 deleted file mode 100644 index f410d42050c..00000000000 --- a/thirdparty/php84/pdo_odbc/odbc_driver.c +++ /dev/null @@ -1,638 +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: 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 deleted file mode 100644 index 8fe11cf2c6a..00000000000 --- a/thirdparty/php84/pdo_odbc/odbc_stmt.c +++ /dev/null @@ -1,865 +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: 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 deleted file mode 100644 index 0893f4b824b..00000000000 --- a/thirdparty/php84/pdo_odbc/php_pdo_odbc_int.h +++ /dev/null @@ -1,179 +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: 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 deleted file mode 100644 index cf98f5f45f9..00000000000 --- a/thirdparty/php84/pdo_pgsql/pgsql_driver.c +++ /dev/null @@ -1,1458 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: 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 deleted file mode 100644 index 5bdea01edc2..00000000000 --- a/thirdparty/php84/pdo_pgsql/pgsql_driver_arginfo.h +++ /dev/null @@ -1,70 +0,0 @@ -/* 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 deleted file mode 100644 index c030abc318b..00000000000 --- a/thirdparty/php84/pdo_pgsql/pgsql_sql_parser.c +++ /dev/null @@ -1,555 +0,0 @@ -/* 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 deleted file mode 100644 index acb21384814..00000000000 --- a/thirdparty/php84/pdo_pgsql/pgsql_statement.c +++ /dev/null @@ -1,740 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: 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 deleted file mode 100644 index c48a6bd9278..00000000000 --- a/thirdparty/php84/pdo_pgsql/php_pdo_pgsql_int.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - +----------------------------------------------------------------------+ - | Copyright (c) The PHP Group | - +----------------------------------------------------------------------+ - | This source file is subject to version 3.01 of the PHP license, | - | that is bundled with this package in the file LICENSE, and is | - | available through the world-wide-web at the following url: | - | https://www.php.net/license/3_01.txt | - | If you did not receive a copy of the PHP license and are unable to | - | obtain it through the world-wide-web, please send a note to | - | license@php.net so we can mail you a copy immediately. | - +----------------------------------------------------------------------+ - | Authors: 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 deleted file mode 100644 index b04f31ec7f0..00000000000 --- a/thirdparty/php84/pdo_sqlite/php_pdo_sqlite_int.h +++ /dev/null @@ -1,84 +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: 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 deleted file mode 100644 index 27d53bbe991..00000000000 --- a/thirdparty/php84/pdo_sqlite/sqlite_driver.c +++ /dev/null @@ -1,825 +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: 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 deleted file mode 100644 index add395f2b99..00000000000 --- a/thirdparty/php84/pdo_sqlite/sqlite_driver.stub.php +++ /dev/null @@ -1,18 +0,0 @@ - | - +----------------------------------------------------------------------+ -*/ - -#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 deleted file mode 100644 index 5f9793e4617..00000000000 --- a/thirdparty/php84/pdo_sqlite/sqlite_statement.c +++ /dev/null @@ -1,381 +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: 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 8aa598440a374d4caa4a631856144ca09d51b79f Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 21 Oct 2024 12:43:51 +0800 Subject: [PATCH 32/52] Fix process shutdown core test (#5529) --- core-tests/src/os/process_pool.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 9685de1ab98..91827e4bb90 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -88,6 +88,7 @@ TEST(process_pool, shutdown) { pool.set_protocol(SW_PROTOCOL_TASK); pool.ptr = shm_value; pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { + SwooleWG.worker->set_max_request(100, 1000); int *shm_value = (int *) pool->ptr; *shm_value = magic_number; usleep(1); From b0aa68f2dba5a78a5f212adc1bf0730ee5f8cac4 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 21 Oct 2024 12:51:31 +0800 Subject: [PATCH 33/52] merge master, optimize Worker::init() & Worker::set_max_request() --- core-tests/src/server/server.cpp | 8 +++----- include/swoole_process_pool.h | 7 +++++-- src/os/process_pool.cc | 22 +++++++++------------- src/server/master.cc | 2 +- src/server/task_worker.cc | 2 +- 5 files changed, 19 insertions(+), 22 deletions(-) diff --git a/core-tests/src/server/server.cpp b/core-tests/src/server/server.cpp index aec005f4f5b..3239e316cee 100644 --- a/core-tests/src/server/server.cpp +++ b/core-tests/src/server/server.cpp @@ -509,14 +509,12 @@ TEST(server, task_worker) { ASSERT_EQ(serv.create(), SW_OK); ASSERT_EQ(serv.create_task_workers(), SW_OK); - SwooleWG.shutdown = false; - SwooleWG.running = true; - SwooleWG.run_always = true; - thread t1([&serv]() { serv.gs->task_workers.running = 1; serv.gs->tasking_num++; - serv.gs->task_workers.main_loop(&serv.gs->task_workers, &serv.gs->task_workers.workers[0]); + Worker *worker = &serv.gs->task_workers.workers[0]; + worker->init(); + serv.gs->task_workers.main_loop(&serv.gs->task_workers, worker); serv.gs->tasking_num--; EXPECT_EQ(serv.get_task_count(), 0); serv.gs->tasking_num--; diff --git a/include/swoole_process_pool.h b/include/swoole_process_pool.h index 329b584fb18..d0a387a9b7a 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 { 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 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 10049a7cd5e..e3a4c4d1a69 100644 --- a/src/os/process_pool.cc +++ b/src/os/process_pool.cc @@ -457,6 +457,7 @@ pid_t ProcessPool::spawn(Worker *worker) { switch (pid) { // child case 0: + worker->init(); worker->pid = SwooleG.pid; SwooleG.process_id = worker->id; SwooleG.process_type = SW_PROCESS_WORKER; @@ -968,11 +969,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(); @@ -981,15 +978,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 15e3b97071b..ba2017bbd50 100644 --- a/src/server/master.cc +++ b/src/server/master.cc @@ -575,7 +575,7 @@ void Server::init_worker(Worker *worker) { } #endif - 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 67fe7af7b6f..1f55d5dd7d2 100644 --- a/src/server/task_worker.cc +++ b/src/server/task_worker.cc @@ -191,7 +191,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); SwooleWG.worker = worker; } From 43b5543a41b525fd8475b255605be41e3a8c6a0a Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 21 Oct 2024 12:52:13 +0800 Subject: [PATCH 34/52] Revert "Fix process shutdown core test (#5529)" This reverts commit 8aa598440a374d4caa4a631856144ca09d51b79f. --- core-tests/src/os/process_pool.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/core-tests/src/os/process_pool.cpp b/core-tests/src/os/process_pool.cpp index 91827e4bb90..9685de1ab98 100644 --- a/core-tests/src/os/process_pool.cpp +++ b/core-tests/src/os/process_pool.cpp @@ -88,7 +88,6 @@ TEST(process_pool, shutdown) { pool.set_protocol(SW_PROTOCOL_TASK); pool.ptr = shm_value; pool.onWorkerStart = [](ProcessPool *pool, Worker *worker) { - SwooleWG.worker->set_max_request(100, 1000); int *shm_value = (int *) pool->ptr; *shm_value = magic_number; usleep(1); From 98314576a43860278f678c1c7e0e8a8ba961b517 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Mon, 21 Oct 2024 13:23:13 +0800 Subject: [PATCH 35/52] Optimize CI --- .github/workflows/alpine.Dockerfile | 20 ++++++++++---------- .github/workflows/core.yml | 1 + .github/workflows/ext.yml | 20 ++++---------------- .github/workflows/unit.yml | 1 + 4 files changed, 16 insertions(+), 26 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/core.yml b/.github/workflows/core.yml index 2bd04f285e0..1c9d4a6cec9 100644 --- a/.github/workflows/core.yml +++ b/.github/workflows/core.yml @@ -9,6 +9,7 @@ jobs: build: runs-on: ubuntu-latest if: "!contains(github.event.head_commit.message, '--filter=') || contains(github.event.head_commit.message, '[core]')" + timeout-minutes: 15 services: tinyproxy: image: "vimagick/tinyproxy" diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index 6806851b32d..de2aadc2b51 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,23 +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.16', '3.17', '3.18', '3.19', 'edge' ] - 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' + php-version: [ '8.0', '8.1', '8.2', '8.3' ] max-parallel: 8 fail-fast: false steps: @@ -94,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 }} diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index eccdbae5bbb..299e205a02b 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 666094673a8c42ecdded56c7cfb7c82afb8a5768 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 22 Oct 2024 08:13:42 +0800 Subject: [PATCH 36/52] [5.1]Fix missing struct members (#5528) * Fix missing struct members * remove code --- scripts/route.sh | 2 +- thirdparty/php/curl/curl_private.h | 2 +- thirdparty/php/curl/interface.cc | 15 ++++++--------- thirdparty/php/curl/multi.cc | 7 ++++++- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/scripts/route.sh b/scripts/route.sh index 07272ae87aa..ae82f2e48c3 100755 --- a/scripts/route.sh +++ b/scripts/route.sh @@ -6,7 +6,7 @@ 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 +if [ ${PHP_VERSION_ID} -lt 80400 ]; then export PHP_VERSION="`php -r "echo PHP_MAJOR_VERSION;"`.`php -r "echo PHP_MINOR_VERSION;"`" else export PHP_VERSION="rc" diff --git a/thirdparty/php/curl/curl_private.h b/thirdparty/php/curl/curl_private.h index b8be8932b59..50c782485c6 100644 --- a/thirdparty/php/curl/curl_private.h +++ b/thirdparty/php/curl/curl_private.h @@ -203,7 +203,7 @@ static inline php_curlsh *curl_share_from_obj(zend_object *obj) { } #define Z_CURL_SHARE_P(zv) curl_share_from_obj(Z_OBJ_P(zv)) -void curl_multi_register_class(const zend_function_entry *method_entries); +void swoole_curl_multi_register_handlers(void); curl_result_t swoole_curl_cast_object(zend_object *obj, zval *result, int type); php_curl *swoole_curl_get_handle(zval *zid, bool exclusive = true, bool required = true); diff --git a/thirdparty/php/curl/interface.cc b/thirdparty/php/curl/interface.cc index a70a63ee3ac..f3142b19349 100644 --- a/thirdparty/php/curl/interface.cc +++ b/thirdparty/php/curl/interface.cc @@ -261,6 +261,9 @@ 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; +#if PHP_VERSION_ID >= 80300 + swoole_coroutine_curl_handle_ce->default_object_handlers = &swoole_coroutine_curl_handle_handlers; +#endif 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; @@ -274,7 +277,7 @@ void swoole_native_curl_minit(int module_number) { zend_declare_property_null(swoole_coroutine_curl_handle_ce, ZEND_STRL("private_data"), ZEND_ACC_PUBLIC); - curl_multi_register_class(nullptr); + swoole_curl_multi_register_handlers(); zend_unregister_functions(swoole_native_curl_functions, -1, CG(function_table)); zend_register_functions(NULL, swoole_native_curl_functions, NULL, MODULE_PERSISTENT); @@ -293,7 +296,9 @@ 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); +#if PHP_VERSION_ID < 80300 intern->std.handlers = &swoole_coroutine_curl_handle_handlers; +#endif return &intern->std; } @@ -907,10 +912,6 @@ 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); @@ -2444,10 +2445,6 @@ static void _php_curl_free(php_curl *ch) { if (--(*ch->clone) == 0) { #if PHP_VERSION_ID < 80100 zend_llist_clean(&ch->to_free->str); -#else -#if LIBCURL_VERSION_NUM < 0x073800 /* 7.56.0 */ - zend_llist_clean(&ch->to_free->buffers); -#endif #endif zend_llist_clean(&ch->to_free->post); zend_llist_clean(&ch->to_free->stream); diff --git a/thirdparty/php/curl/multi.cc b/thirdparty/php/curl/multi.cc index 52f6ccb6931..9dc0f9f1337 100644 --- a/thirdparty/php/curl/multi.cc +++ b/thirdparty/php/curl/multi.cc @@ -551,7 +551,9 @@ static zend_object *swoole_curl_multi_create_object(zend_class_entry *class_type zend_object_std_init(&intern->std, class_type); object_properties_init(&intern->std, class_type); +#if PHP_VERSION_ID < 80300 intern->std.handlers = &swoole_coroutine_curl_multi_handle_handlers; +#endif return &intern->std; } @@ -600,9 +602,12 @@ static HashTable *swoole_curl_multi_get_gc(zend_object *object, zval **table, in return zend_std_get_properties(object); } -void curl_multi_register_class(const zend_function_entry *method_entries) { +void swoole_curl_multi_register_handlers(void) { swoole_coroutine_curl_multi_handle_ce = curl_multi_ce; swoole_coroutine_curl_multi_handle_ce->create_object = swoole_curl_multi_create_object; +#if PHP_VERSION_ID >= 80300 + swoole_coroutine_curl_multi_handle_ce->default_object_handlers = &swoole_coroutine_curl_multi_handle_handlers; +#endif 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); From c713f73211c9f896e86e6f1744bff7f9fed9ee6f Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 22 Oct 2024 09:28:20 +0800 Subject: [PATCH 37/52] [5.1]v5.1.5 release (#5532) * release v5.1.5 --- CMakeLists.txt | 2 +- include/swoole_version.h | 2 +- package.xml | 29 ++++++++++++++++++++--------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e55521b8e0..7d52777f3b5 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 5.1.5-dev) +set(SWOOLE_VERSION 5.1.5) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index c28b616dac1..4bf3637d3fe 100644 --- a/include/swoole_version.h +++ b/include/swoole_version.h @@ -22,7 +22,7 @@ #define SWOOLE_MINOR_VERSION 1 #define SWOOLE_RELEASE_VERSION 5 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.5-dev" +#define SWOOLE_VERSION "5.1.5" #define SWOOLE_VERSION_ID 50105 #define SWOOLE_API_VERSION_ID 0x202208a diff --git a/package.xml b/package.xml index ab07241349d..1184f49fe40 100644 --- a/package.xml +++ b/package.xml @@ -51,10 +51,10 @@ doubaokun@php.net yes - 2024-08-22 - + 2024-10-22 + - 5.1.4 + 5.1.5 5.0 @@ -63,11 +63,15 @@ Apache2.0 - - Fix broken build with GCC 14. @remicollet - - Fix could not send SSL negotiation packet(Resource temporarily unavailable). @NathanFreeman - - Fix the issue where certain critical parameters of `Swoole\Server` are not reset to 0 during process restart. @NathanFreeman - - Fix the problem where `Swoole\Http\Request::getMethod()` returns the incorrect request method when `HTTP2` is enabled. @matyhtf - - Optimize `Swoole\Http\Response::end()`. Response data larger than 16K will be sent directly through the socket, while data smaller than or equal to 16K will be copied to the buffer first before being sent out via the socket. @NathanFreeman + - Fix the need to use `zend_ini_parse_quantity` to parse string numbers for PHP versions greater than 8.2. @matyhtf + - Fix an occasional resource unavailability issue when coroutineizing `pdo_pgsql`. @NathanFreeman + - Fix header file reference issues when coroutineizing `pdo_pgsql`. @NathanFreeman + - Fix incorrect relative path checks to avoid bypassing path validation. @matyhtf + - Fix incorrect concurrency count caused by process restarts in high-concurrency environments. @matyhtf + - Sync some related code for `php8.3 curl`. @NathanFreeman + - Fix core test errors in the `process` module. @NathanFreeman + - In `SWOOLE_BASE` mode, all connections should be closed during the `PHP RSHUTDOWN` phase. @matyhtf + - Optimize kernel code. @matyhtf @@ -76,6 +80,7 @@ + @@ -1494,6 +1499,8 @@ + + @@ -1511,8 +1518,11 @@ + + + @@ -2129,6 +2139,7 @@ + @@ -2575,7 +2586,7 @@ swoole - + From ae12f42de4006d380643a0a79d6a578914666c7c Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Tue, 22 Oct 2024 10:10:58 +0200 Subject: [PATCH 38/52] Disallow PHP 8.4 (#5535) --- package.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.xml b/package.xml index 1184f49fe40..f9090605113 100644 --- a/package.xml +++ b/package.xml @@ -2578,9 +2578,10 @@ 8.0.0 + 8.3.99 - 1.4.0 + 1.10.0 From 8d44c7e19e4d583ec90e65a64d49029cf1dcec31 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:53:34 +0800 Subject: [PATCH 39/52] [5.1]Fix send chunk error (#5567) * fix send chunk error * Update chunk.phpt --------- Co-authored-by: Tianfeng.Han --- ext-src/swoole_http_request.cc | 4 ---- ext-src/swoole_http_response.cc | 5 +++-- tests/swoole_http_server/chunk.phpt | 4 ++-- tests/swoole_http_server/trailer.phpt | 6 +++--- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ext-src/swoole_http_request.cc b/ext-src/swoole_http_request.cc index 658a34d303f..371fe0d7bdc 100644 --- a/ext-src/swoole_http_request.cc +++ b/ext-src/swoole_http_request.cc @@ -845,10 +845,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)) { diff --git a/ext-src/swoole_http_response.cc b/ext-src/swoole_http_response.cc index d4a663f5580..6c2e49d303d 100644 --- a/ext-src/swoole_http_response.cc +++ b/ext-src/swoole_http_response.cc @@ -746,6 +746,7 @@ bool HttpContext::send_file(const char *file, uint32_t l_file, off_t offset, siz void HttpContext::end(zval *zdata, zval *return_value) { char *data = nullptr; size_t length = zdata ? php_swoole_get_send_data(zdata, &data) : 0; + String *http_buffer = nullptr; if (send_chunked) { if (send_trailer_) { @@ -760,7 +761,7 @@ void HttpContext::end(zval *zdata, zval *return_value) { } } send_chunked = 0; - return; + goto _skip_copy; } #ifdef SW_HAVE_ZLIB @@ -790,7 +791,7 @@ void HttpContext::end(zval *zdata, zval *return_value) { } #endif - String *http_buffer = get_write_buffer(); + http_buffer = get_write_buffer(); http_buffer->clear(); build_header(http_buffer, data, length); diff --git a/tests/swoole_http_server/chunk.phpt b/tests/swoole_http_server/chunk.phpt index be33665b4e4..b696fa94dbd 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 444c4259eea3ab1947b7efd05ead094d2ca21e79 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:53:57 +0800 Subject: [PATCH 40/52] Fix table (#5572) --- 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 585faa63839..a8ac8d624e7 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 84d9b519b95d75a69919f78ba4e10408409456df Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:56:41 +0800 Subject: [PATCH 41/52] Fix bug #5552 (#5570) --- ext-src/swoole_runtime.cc | 17 +++--- ext-src/swoole_socket_coro.cc | 11 ++-- include/swoole_coroutine_socket.h | 3 +- include/swoole_error.h | 1 + src/core/base.cc | 6 +++ src/coroutine/socket.cc | 21 +++++--- .../{ => ssl}/capture_peer_cert.phpt | 0 .../{ssl_client.phpt => ssl/client.phpt} | 0 tests/swoole_runtime/ssl/enable_crypto.phpt | 54 +++++++++++++++++++ .../{ssl_server.phpt => ssl/server.phpt} | 0 tests/swoole_server_coro/ssl.phpt | 3 +- 11 files changed, 88 insertions(+), 28 deletions(-) rename tests/swoole_runtime/{ => ssl}/capture_peer_cert.phpt (100%) rename tests/swoole_runtime/{ssl_client.phpt => ssl/client.phpt} (100%) create mode 100644 tests/swoole_runtime/ssl/enable_crypto.phpt rename tests/swoole_runtime/{ssl_server.phpt => ssl/server.phpt} (100%) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index a7856f401b4..b9e31e74c4f 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -754,28 +754,25 @@ 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; + sock->ssl_shutdown(); + return -1; } - if (context) { + 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 0; + return 1; } #endif @@ -1030,11 +1027,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 a78ece50e3d..bb42ee32726 100644 --- a/ext-src/swoole_socket_coro.cc +++ b/ext-src/swoole_socket_coro.cc @@ -1133,7 +1133,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); @@ -1150,7 +1149,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)) { @@ -1159,7 +1158,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()) { @@ -1206,11 +1205,7 @@ SW_API bool php_swoole_socket_set_ssl(Socket *sock, zval *zset) { 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 d22937fb807..2d2a37c41a7 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_check_context(); bool ssl_handshake(); bool ssl_verify(bool allow_self_signed); std::string ssl_get_peer_cert(); @@ -442,7 +441,9 @@ 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); + bool ssl_listen(); #endif bool connected = false; diff --git a/include/swoole_error.h b/include/swoole_error.h index 603d0cd38bc..8230c9fde0e 100644 --- a/include/swoole_error.h +++ b/include/swoole_error.h @@ -73,6 +73,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/core/base.cc b/src/core/base.cc index 78d09df331a..81429beb599 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -275,6 +275,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 a141d2b1ebe..7e4d21efb80 100644 --- a/src/coroutine/socket.cc +++ b/src/coroutine/socket.cc @@ -1220,6 +1220,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); @@ -1245,10 +1250,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; @@ -1261,7 +1263,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; @@ -1295,12 +1296,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/capture_peer_cert.phpt b/tests/swoole_runtime/ssl/capture_peer_cert.phpt similarity index 100% rename from tests/swoole_runtime/capture_peer_cert.phpt rename to tests/swoole_runtime/ssl/capture_peer_cert.phpt diff --git a/tests/swoole_runtime/ssl_client.phpt b/tests/swoole_runtime/ssl/client.phpt similarity index 100% rename from tests/swoole_runtime/ssl_client.phpt rename to tests/swoole_runtime/ssl/client.phpt diff --git a/tests/swoole_runtime/ssl/enable_crypto.phpt b/tests/swoole_runtime/ssl/enable_crypto.phpt new file mode 100644 index 00000000000..649eed7e6ce --- /dev/null +++ b/tests/swoole_runtime/ssl/enable_crypto.phpt @@ -0,0 +1,54 @@ +--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 diff --git a/tests/swoole_runtime/ssl_server.phpt b/tests/swoole_runtime/ssl/server.phpt similarity index 100% rename from tests/swoole_runtime/ssl_server.phpt rename to tests/swoole_runtime/ssl/server.phpt 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 0f8f2be4c173701ab36540e926c797b681879b92 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 20 Nov 2024 08:56:57 +0800 Subject: [PATCH 42/52] Fix bug #5558 (#5571) --- ext-src/swoole_server.cc | 3 +- tests/swoole_server/named_parameters.phpt | 46 +++++++++++++++++++++++ 2 files changed, 48 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 52f425a567d..d52bc4ca85e 100644 --- a/ext-src/swoole_server.cc +++ b/ext-src/swoole_server.cc @@ -3809,7 +3809,7 @@ static PHP_METHOD(swoole_server, stop) { } zend_bool wait_reactor = 0; - zend_long worker_id = SwooleG.process_id; + zend_long worker_id = -1; ZEND_PARSE_PARAMETERS_START(0, 2) Z_PARAM_OPTIONAL @@ -3817,6 +3817,7 @@ static PHP_METHOD(swoole_server, stop) { Z_PARAM_BOOL(wait_reactor) ZEND_PARSE_PARAMETERS_END_EX(RETURN_FALSE); + worker_id = worker_id < 0 ? SwooleG.process_id : worker_id; if (worker_id == SwooleG.process_id && wait_reactor == 0) { if (SwooleTG.reactor != nullptr) { SwooleTG.reactor->defer( diff --git a/tests/swoole_server/named_parameters.phpt b/tests/swoole_server/named_parameters.phpt new file mode 100644 index 00000000000..c5f433aabc0 --- /dev/null +++ b/tests/swoole_server/named_parameters.phpt @@ -0,0 +1,46 @@ +--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 5942dc5f99194dc61bade786144e058387c45391 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:35:05 +0800 Subject: [PATCH 43/52] Fix zend_max_execution_timer.h cannot be included by the C++ extension. (#5585) --- ext-src/php_swoole.cc | 15 +++++++++++---- include/swoole_c_api.h | 1 + php_swoole.h | 8 ++++++++ src/core/base.cc | 4 ++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/ext-src/php_swoole.cc b/ext-src/php_swoole.cc index 3dab63ba1ab..a8b249e2b7e 100644 --- a/ext-src/php_swoole.cc +++ b/ext-src/php_swoole.cc @@ -17,13 +17,13 @@ #include "php_swoole_library.h" #include "php_swoole_process.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" @@ -990,6 +990,12 @@ const swoole::Allocator *sw_zend_string_allocator() { return &zend_string_allocator; } +static void sw_after_fork(void *args) { +#ifdef ZEND_MAX_EXECUTION_TIMERS + zend_max_execution_timer_init(); +#endif +} + PHP_RINIT_FUNCTION(swoole) { if (!SWOOLE_G(cli)) { return SUCCESS; @@ -1030,6 +1036,7 @@ PHP_RINIT_FUNCTION(swoole) { /* Disable warning even in ZEND_DEBUG because we may register our own signal handlers */ SIGG(check) = 0; #endif + swoole_add_hook(SW_GLOBAL_HOOK_AFTER_FORK, sw_after_fork, 0); php_swoole_http_server_rinit(); php_swoole_coroutine_rinit(); diff --git a/include/swoole_c_api.h b/include/swoole_c_api.h index 28e27839068..37c1211d56c 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 aa6cb096f06..42e9ae3878e 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 81429beb599..2b81a31e5d2 100644 --- a/src/core/base.cc +++ b/src/core/base.cc @@ -423,6 +423,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 3c7d3f5593ed107e6d18466afcd84c481f77507a Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:40:20 +0800 Subject: [PATCH 44/52] Fix bug #5547 (#5586) --- core-tests/src/network/dns.cpp | 16 +-------- include/swoole_async.h | 36 ++++++++++++++++--- include/swoole_socket.h | 15 ++------ src/coroutine/system.cc | 24 ++++--------- src/network/client.cc | 14 +++----- src/network/dns.cc | 29 ++++++++------- src/os/base.cc | 10 +++--- tests/include/functions.php | 8 +++++ tests/include/skipif.inc | 2 +- .../swoole_coroutine_system/getaddrinfo.phpt | 2 +- .../getaddrinfo_timeout.phpt | 30 ++++++++++++++++ tests/swoole_http_server/bug_5186.phpt | 2 +- 12 files changed, 107 insertions(+), 81 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..76c6a4efc2f 100644 --- a/core-tests/src/network/dns.cpp +++ b/core-tests/src/network/dns.cpp @@ -74,23 +74,9 @@ 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); - - vector ip_list; - req.parse_result(ip_list); - - for (auto &ip : ip_list) { - ASSERT_TRUE(swoole::network::Address::verify_ip(AF_INET, ip)); - } } TEST(dns, load_resolv_conf) { diff --git a/include/swoole_async.h b/include/swoole_async.h index f5c718d2c0d..3ab5f5c745c 100644 --- a/include/swoole_async.h +++ b/include/swoole_async.h @@ -34,13 +34,17 @@ enum AsyncFlag { SW_AIO_EOF = 1u << 2, }; +struct AsyncRequest { + virtual ~AsyncRequest() = default; +}; + struct AsyncEvent { size_t task_id; uint8_t canceled; /** * input & output */ - void *data; + std::shared_ptr data; /** * output */ @@ -60,22 +64,44 @@ 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: bool schedule = false; diff --git a/include/swoole_socket.h b/include/swoole_socket.h index 1626d8d09d0..c1990adea0b 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/src/coroutine/system.cc b/src/coroutine/system.cc index 001d66592f3..087e8712ced 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..6706defa2ce 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 9d6c2d10576..5b30d56d141 100644 --- a/src/os/base.cc +++ b/src/os/base.cc @@ -85,14 +85,14 @@ namespace async { void handler_gethostbyname(AsyncEvent *event) { char addr[INET6_ADDRSTRLEN]; - auto 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 { @@ -104,7 +104,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 4dd94504cad..d86b5e97ff5 100644 --- a/tests/include/functions.php +++ b/tests/include/functions.php @@ -852,3 +852,11 @@ function build_ftp_url(string $path = ''): string { return 'ftp://' . FTP_USER . ':' . FTP_PASS . '@' . FTP_HOST . ':' . FTP_PORT . '/' . $path; } + +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 faf324acd13..1f731049d36 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 e4add248ae9665fa815b695aec90dbf7cbb374db Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Tue, 26 Nov 2024 08:46:31 +0800 Subject: [PATCH 45/52] Fix issue of unexpected response timeouts when an HTTP client with long time keep-alive (#5588) --- ext-src/swoole_http_client_coro.cc | 19 +++++++++++-------- ext-src/swoole_mysql_coro.cc | 4 ++-- include/swoole_coroutine_socket.h | 29 +++++++++++++++-------------- 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index 4746b332bf6..e79a7aa613d 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); } @@ -806,11 +809,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); @@ -1368,9 +1371,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/ext-src/swoole_mysql_coro.cc b/ext-src/swoole_mysql_coro.cc index bcfd41a0c61..f09acdd1286 100644 --- a/ext-src/swoole_mysql_coro.cc +++ b/ext-src/swoole_mysql_coro.cc @@ -65,7 +65,7 @@ class MysqlClient { Socket *socket = nullptr; zval zsocket; zval zobject; - Socket::timeout_controller *tc = nullptr; + Socket::TimeoutController *tc = nullptr; enum sw_mysql_state state = SW_MYSQL_STATE_CLOSED; bool quit = false; @@ -174,7 +174,7 @@ class MysqlClient { // Notice: `timeout > 0` is wrong, maybe -1 if (timeout != 0) { SW_ASSERT(!tc); - tc = new Socket::timeout_controller(socket, timeout, type); + tc = new Socket::TimeoutController(socket, timeout, type); } } diff --git a/include/swoole_coroutine_socket.h b/include/swoole_coroutine_socket.h index 2d2a37c41a7..f5415b0631b 100644 --- a/include/swoole_coroutine_socket.h +++ b/include/swoole_coroutine_socket.h @@ -512,8 +512,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; @@ -545,16 +545,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]); } } } @@ -579,12 +579,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(); @@ -594,7 +595,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 df1f708b22440110aff8c125244d3de56569ed8c Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Thu, 28 Nov 2024 00:57:58 +0800 Subject: [PATCH 46/52] release v5.1.6 (#5592) --- .github/workflows/ext.yml | 2 +- CMakeLists.txt | 2 +- include/swoole_version.h | 6 +++--- package.xml | 31 ++++++++++++++++--------------- 4 files changed, 21 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ext.yml b/.github/workflows/ext.yml index de2aadc2b51..7b25873a805 100644 --- a/.github/workflows/ext.yml +++ b/.github/workflows/ext.yml @@ -45,7 +45,7 @@ jobs: runs-on: macos-latest steps: - name: install dependencies - run: brew reinstall php + run: brew reinstall php@8.3 && brew link php@8.3 --force - uses: actions/checkout@v4 - name: phpize run: phpize diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d52777f3b5..31dc1ab6faf 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 5.1.5) +set(SWOOLE_VERSION 5.1.6) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -g") diff --git a/include/swoole_version.h b/include/swoole_version.h index 4bf3637d3fe..9b9a34d6525 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 5 +#define SWOOLE_RELEASE_VERSION 6 #define SWOOLE_EXTRA_VERSION "" -#define SWOOLE_VERSION "5.1.5" -#define SWOOLE_VERSION_ID 50105 +#define SWOOLE_VERSION "5.1.6" +#define SWOOLE_VERSION_ID 50106 #define SWOOLE_API_VERSION_ID 0x202208a #define SWOOLE_BUG_REPORT \ diff --git a/package.xml b/package.xml index f9090605113..9c491f50901 100644 --- a/package.xml +++ b/package.xml @@ -51,10 +51,10 @@ doubaokun@php.net yes - 2024-10-22 - + 2024-11-27 + - 5.1.5 + 5.1.6 5.0 @@ -63,15 +63,13 @@ Apache2.0 - - Fix the need to use `zend_ini_parse_quantity` to parse string numbers for PHP versions greater than 8.2. @matyhtf - - Fix an occasional resource unavailability issue when coroutineizing `pdo_pgsql`. @NathanFreeman - - Fix header file reference issues when coroutineizing `pdo_pgsql`. @NathanFreeman - - Fix incorrect relative path checks to avoid bypassing path validation. @matyhtf - - Fix incorrect concurrency count caused by process restarts in high-concurrency environments. @matyhtf - - Sync some related code for `php8.3 curl`. @NathanFreeman - - Fix core test errors in the `process` module. @NathanFreeman - - In `SWOOLE_BASE` mode, all connections should be closed during the `PHP RSHUTDOWN` phase. @matyhtf - - Optimize kernel code. @matyhtf + - Fixed the issue where `Swoole\Http\Response::end()` returns `null`. @NathanFreeman + - Fixed the problem where the mutex lock of `Swoole\Table` could not be used before the process exits. @matyhtf + - Fixed the failure of `Swoole\Server::stop()` caused by using named parameters. @matyhtf + - Fixed the issue where the `runtime tcp` module did not support dynamically enabling SSL encryption. @matyhtf + - Fixed the `Fatal error` issue caused by the timeout feature of `PHP` in `ZTS` mode. @matyhtf + - Fixed the problem where `Swoole\Coroutine::getaddrinfo()` method could lead to SIGSEGV. @matyhtf + - Fixed the issue where the HTTP client running for a long time resulted in incorrect timeout settings. @matyhtf @@ -1135,6 +1133,7 @@ + @@ -1892,7 +1891,6 @@ - @@ -1993,10 +1991,12 @@ + + + + - - @@ -2128,6 +2128,7 @@ + From 8fe696af948d0d93c3d2a0a3aed0e7c6193f227d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=AE=A3=E8=A8=80=E5=B0=B1=E6=98=AFSiam?= <59419979@qq.com> Date: Fri, 29 Nov 2024 20:55:08 +0800 Subject: [PATCH 47/52] Fix bug #5382 (#5383) (#5597) Co-authored-by: guandeng 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 434ac06df4d76150b21be072f12dd4c3271f1857 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:55:01 +0800 Subject: [PATCH 48/52] fix oracle database name error (#5608) --- scripts/docker-compose.yml | 2 +- tests/include/config.php | 2 +- tests/swoole_pdo_oracle/bug44301.phpt | 2 +- tests/swoole_pdo_oracle/bug_33707.phpt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml index 74bc9234312..47d8451c35a 100755 --- a/scripts/docker-compose.yml +++ b/scripts/docker-compose.yml @@ -39,7 +39,7 @@ services: POSTGRES_DB: test POSTGRES_PASSWORD: root oracle: - image: gvenzl/oracle-xe:slim + image: gvenzl/oracle-free:slim container_name: "oracle" environment: ORACLE_PASSWORD: oracle diff --git a/tests/include/config.php b/tests/include/config.php index e0e27f51bb7..0abbed2563e 100644 --- a/tests/include/config.php +++ b/tests/include/config.php @@ -78,7 +78,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 fdca88ffc407abf4a2653377db53db631d7b5790 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Mon, 9 Dec 2024 16:55:21 +0800 Subject: [PATCH 49/52] fix 301 status code test (#5609) --- tests/swoole_http_client_coro/disable_keep_alive.phpt | 6 +++--- tests/swoole_http_client_coro/multi_and_reuse.phpt | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) 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); From 0ce87730fc3d96344bc465dcdea6ba27d4957312 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 27 Dec 2024 16:09:11 +0800 Subject: [PATCH 50/52] Optimize runtime hook, print error log when exception occurs, copy arginfo once, fix memory error when fatal error occurs --- ext-src/swoole_runtime.cc | 54 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index b9e31e74c4f..c964907ac92 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -163,6 +163,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) \ @@ -1163,6 +1196,11 @@ void PHPCoroutine::enable_unsafe_function() { } } +static void hook_stream_throw_exception(const char *type) { + swoole_error_log( + SW_LOG_WARNING, SW_ERROR_PHP_FATAL_ERROR, "failed to register `%s` stream transport factory", type); +} + 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); @@ -1189,6 +1227,7 @@ bool PHPCoroutine::enable_hook(uint32_t flags) { 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 { @@ -1200,6 +1239,7 @@ bool PHPCoroutine::enable_hook(uint32_t 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 { @@ -1211,6 +1251,7 @@ bool PHPCoroutine::enable_hook(uint32_t 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 { @@ -1222,6 +1263,7 @@ bool PHPCoroutine::enable_hook(uint32_t 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 { @@ -1233,6 +1275,7 @@ bool PHPCoroutine::enable_hook(uint32_t 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 { @@ -1248,6 +1291,7 @@ bool PHPCoroutine::enable_hook(uint32_t 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 { @@ -1370,8 +1414,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) { @@ -1380,6 +1424,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); @@ -1445,6 +1490,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"); @@ -1505,7 +1551,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); @@ -1948,7 +1994,7 @@ static void hook_func(const char *name, size_t l_name, zif_handler handler, zend rf->ori_arg_info = zf->internal_function.arg_info; zf->internal_function.handler = handler; if (arg_info) { - zf->internal_function.arg_info = arg_info; + zf->internal_function.arg_info = copy_arginfo(zf, arg_info); } if (use_php_func) { From a0a1ed81319c2a7d8b85875384cbcf0a68d42ae4 Mon Sep 17 00:00:00 2001 From: matyhtf Date: Fri, 27 Dec 2024 16:27:24 +0800 Subject: [PATCH 51/52] Fix arginfo memory leak --- ext-src/swoole_runtime.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ext-src/swoole_runtime.cc b/ext-src/swoole_runtime.cc index c964907ac92..c17e6685c98 100644 --- a/ext-src/swoole_runtime.cc +++ b/ext-src/swoole_runtime.cc @@ -257,6 +257,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_fcall_info_cache *fci_cache; @@ -1995,6 +1996,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) { @@ -2022,6 +2024,10 @@ static void unhook_func(const char *name, size_t l_name) { if (rf == nullptr) { return; } + if (rf->arg_info_copy) { + zend_free_internal_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; } From 852f51523bb07d603d04ba52dd0b22eb25409739 Mon Sep 17 00:00:00 2001 From: MARiA so cute <33935209+NathanFreeman@users.noreply.github.com> Date: Wed, 1 Jan 2025 08:24:26 +0800 Subject: [PATCH 52/52] Fix bug #5635 (#5643) --- ext-src/swoole_pgsql.cc | 17 ++++++- tests/swoole_pdo_pgsql/bug_5635.phpt | 71 ++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 2 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 6c76f71f70e..0e7016bf12a 100644 --- a/ext-src/swoole_pgsql.cc +++ b/ext-src/swoole_pgsql.cc @@ -30,7 +30,7 @@ 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) { +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); @@ -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