From 38762257636ee69468ec27c0e74aa7584534038b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sat, 27 Apr 2024 23:45:48 +0200 Subject: [PATCH 001/201] Fix casing of NULL default values --- ext/ldap/ldap.stub.php | 4 ++-- ext/ldap/ldap_arginfo.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ext/ldap/ldap.stub.php b/ext/ldap/ldap.stub.php index ecda9be7496a9..e10cbc6e508a6 100644 --- a/ext/ldap/ldap.stub.php +++ b/ext/ldap/ldap.stub.php @@ -790,12 +790,12 @@ function ldap_8859_to_t61(string $value): string|false {} * @param string $response_data * @param string $response_oid */ - function ldap_exop(LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = NULL, &$response_data = UNKNOWN, &$response_oid = null): LDAP\Result|bool {} + function ldap_exop(LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = UNKNOWN, &$response_oid = null): LDAP\Result|bool {} /** * @param string $response_data * @param string $response_oid */ - function ldap_exop_sync(LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = NULL, &$response_data = null, &$response_oid = null): LDAP\Result|bool {} + function ldap_exop_sync(LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null): LDAP\Result|bool {} #endif #ifdef HAVE_LDAP_PASSWD diff --git a/ext/ldap/ldap_arginfo.h b/ext/ldap/ldap_arginfo.h index 8993ccb994edb..ed039381762af 100644 --- a/ext/ldap/ldap_arginfo.h +++ b/ext/ldap/ldap_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: bd41c14d3e28c21238365a28d1ee9806521719af */ + * Stub hash: 49801a700489ac5c4ffb8c3916278ec7aa002ab6 */ #if defined(HAVE_ORALDAP) ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ldap_connect, 0, 0, LDAP\\Connection, MAY_BE_FALSE) @@ -310,7 +310,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ldap_exop, 0, 2, LDAP\\Resul ZEND_ARG_OBJ_INFO(0, ldap, LDAP\\Connection, 0) ZEND_ARG_TYPE_INFO(0, request_oid, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, request_data, IS_STRING, 1, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, controls, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, controls, IS_ARRAY, 1, "null") ZEND_ARG_INFO(1, response_data) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, response_oid, "null") ZEND_END_ARG_INFO() @@ -321,7 +321,7 @@ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_ldap_exop_sync, 0, 2, LDAP\\ ZEND_ARG_OBJ_INFO(0, ldap, LDAP\\Connection, 0) ZEND_ARG_TYPE_INFO(0, request_oid, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, request_data, IS_STRING, 1, "null") - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, controls, IS_ARRAY, 1, "NULL") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, controls, IS_ARRAY, 1, "null") ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, response_data, "null") ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, response_oid, "null") ZEND_END_ARG_INFO() From acdf63b999add1ddea4670ddfcdbfdfa9eeb4eab Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 27 Apr 2024 19:46:37 +0200 Subject: [PATCH 002/201] Get rid of error gotos in HTML5 parse conversion --- ext/dom/html5_parser.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c index 973586155078c..794050a33ed94 100644 --- a/ext/dom/html5_parser.c +++ b/ext/dom/html5_parser.c @@ -132,7 +132,7 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( xmlNodePtr lxml_element = xmlNewDocNode(lxml_doc, NULL, name, NULL); if (UNEXPECTED(lxml_element == NULL)) { retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; - goto out; + break; } xmlAddChild(lxml_parent, lxml_element); lxml_element->line = sanitize_line_nr(node->line); @@ -175,13 +175,13 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( if (UNEXPECTED(local_name_length >= INT_MAX || value_length >= INT_MAX)) { retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OVERFLOW; - goto out; + break; } xmlAttrPtr lxml_attr = xmlMalloc(sizeof(xmlAttr)); if (UNEXPECTED(lxml_attr == NULL)) { retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; - goto out; + break; } memset(lxml_attr, 0, sizeof(xmlAttr)); @@ -193,7 +193,7 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( if (UNEXPECTED(lxml_text == NULL)) { xmlFreeProp(lxml_attr); retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; - goto out; + break; } lxml_attr->children = lxml_attr->last = lxml_text; @@ -238,12 +238,12 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( size_t data_length = text->char_data.data.length; if (UNEXPECTED(data_length >= INT_MAX)) { retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OVERFLOW; - goto out; + break; } xmlNodePtr lxml_text = lexbor_libxml2_bridge_new_text_node_fast(lxml_doc, data, data_length, compact_text_nodes); if (UNEXPECTED(lxml_text == NULL)) { retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; - goto out; + break; } xmlAddChild(lxml_parent, lxml_text); if (node->line >= USHRT_MAX) { @@ -266,7 +266,7 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( ); if (UNEXPECTED(lxml_dtd == NULL)) { retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; - goto out; + break; } /* libxml2 doesn't support line numbers on this anyway, it returns -1 instead, so don't bother */ } else if (node->type == LXB_DOM_NODE_TYPE_COMMENT) { @@ -274,14 +274,13 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( xmlNodePtr lxml_comment = xmlNewDocComment(lxml_doc, comment->char_data.data.data); if (UNEXPECTED(lxml_comment == NULL)) { retval = LEXBOR_LIBXML2_BRIDGE_STATUS_OOM; - goto out; + break; } xmlAddChild(lxml_parent, lxml_comment); lxml_comment->line = sanitize_line_nr(node->line); } } -out: lexbor_array_obj_destroy(&work_list, false); return retval; } From a86e6687640c3ef8c32a86034ab2cf46b9917909 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 27 Apr 2024 19:46:57 +0200 Subject: [PATCH 003/201] Improve performance of text node allocation by marking the text node construction as always inline --- ext/dom/html5_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c index 794050a33ed94..87e62d6fd88d0 100644 --- a/ext/dom/html5_parser.c +++ b/ext/dom/html5_parser.c @@ -73,7 +73,7 @@ static const php_dom_ns_magic_token *get_libxml_namespace_href(uintptr_t lexbor_ } } -static xmlNodePtr lexbor_libxml2_bridge_new_text_node_fast(xmlDocPtr lxml_doc, const lxb_char_t *data, size_t data_length, bool compact_text_nodes) +static zend_always_inline xmlNodePtr lexbor_libxml2_bridge_new_text_node_fast(xmlDocPtr lxml_doc, const lxb_char_t *data, size_t data_length, bool compact_text_nodes) { if (compact_text_nodes && data_length < LXML_INTERNED_STRINGS_SIZE) { /* See xmlSAX2TextNode() in libxml2 */ From b0da6ed6eeec6a659c16369bc33386cc120af732 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 28 Apr 2024 11:40:54 +0200 Subject: [PATCH 004/201] Be specific for the namespace of the id attribute (#14060) --- ext/dom/html5_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/dom/html5_parser.c b/ext/dom/html5_parser.c index 87e62d6fd88d0..13fbbe58149f5 100644 --- a/ext/dom/html5_parser.c +++ b/ext/dom/html5_parser.c @@ -226,7 +226,7 @@ static lexbor_libxml2_bridge_status lexbor_libxml2_bridge_convert( last_added_attr = lxml_attr; /* xmlIsID does some other stuff too that is irrelevant here. */ - if (local_name_length == 2 && local_name[0] == 'i' && local_name[1] == 'd') { + if (local_name_length == 2 && local_name[0] == 'i' && local_name[1] == 'd' && attr->node.ns == LXB_NS_HTML) { xmlAddID(NULL, lxml_doc, value, lxml_attr); } From b147a22bb87508f0b6ef2f59731a4bcccbba7b35 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 28 Apr 2024 10:33:28 +0100 Subject: [PATCH 005/201] ext/sockets: adding SO_NOSIGPIPE constant. it s the macOs way to disable the SIGPIPE signal emission, same as doing `signal(SIGPIPE, SIG_IGN)` but on the socket level. Close GH-14065 --- NEWS | 2 ++ UPGRADING | 1 + ext/sockets/sockets.stub.php | 7 +++++++ ext/sockets/sockets_arginfo.h | 5 ++++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index c35b3634dbcff..159d91b460739 100644 --- a/NEWS +++ b/NEWS @@ -209,6 +209,8 @@ PHP NEWS (David Carlier) . Updated the socket_create_listen backlog argument default value to SOMAXCONN. (David Carlier) + . Added the SO_NOSIGPIPE constant to control the generation of SIGPIPE for macOs. + (David Carlier) - SNMP: . Removed the deprecated inet_ntoa call support. (David Carlier) diff --git a/UPGRADING b/UPGRADING index 7c93e6f57b659..a6835b501495f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -611,6 +611,7 @@ PHP 8.4 UPGRADE NOTES . SOCK_DCCP (NetBSD only). . TCP_SYNCNT (Linux only). . SO_EXCLBIND (Solaris/Illumos only). + . SO_NOSIGPIPE (macOs only). - Sodium: . SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index 00062610b7ea7..0b48d7109064b 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -1782,6 +1782,13 @@ */ const SO_EXCLUSIVEADDRUSE = UNKNOWN; #endif +#if defined(SO_NOSIGPIPE) +/** + * @var int + * @cvalue SO_NOSIGPIPE + */ +const SO_NOSIGPIPE = UNKNOWN; +#endif #if defined(TCP_QUICKACK) /** * @var int diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index 50c5e114c8ed4..30a5aade8a0c0 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 88a8e188f73c18c510eb984586b21109b347f251 */ + * Stub hash: 70a996a13d739c1d2d165c73427cff59ebae1c5f */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_socket_select, 0, 4, MAY_BE_LONG|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(1, read, IS_ARRAY, 1) @@ -1020,6 +1020,9 @@ static void register_sockets_symbols(int module_number) #if defined(SO_EXCLUSIVEADDRUSE) REGISTER_LONG_CONSTANT("SO_EXCLUSIVEADDRUSE", SO_EXCLUSIVEADDRUSE, CONST_PERSISTENT); #endif +#if defined(SO_NOSIGPIPE) + REGISTER_LONG_CONSTANT("SO_NOSIGPIPE", SO_NOSIGPIPE, CONST_PERSISTENT); +#endif #if defined(TCP_QUICKACK) REGISTER_LONG_CONSTANT("TCP_QUICKACK", TCP_QUICKACK, CONST_PERSISTENT); #endif From d5d227a43a2743c0b4fe63dce02523ccd77c2270 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sun, 28 Apr 2024 13:01:49 +0100 Subject: [PATCH 006/201] [ci skip] ext/sockets: followup on #14065. (#14066) freebsd supports SO_NOSIGPIPE too. --- NEWS | 4 ++-- UPGRADING | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 159d91b460739..c47383edb5463 100644 --- a/NEWS +++ b/NEWS @@ -209,8 +209,8 @@ PHP NEWS (David Carlier) . Updated the socket_create_listen backlog argument default value to SOMAXCONN. (David Carlier) - . Added the SO_NOSIGPIPE constant to control the generation of SIGPIPE for macOs. - (David Carlier) + . Added the SO_NOSIGPIPE constant to control the generation of SIGPIPE for + macOs and FreeBSD. (David Carlier) - SNMP: . Removed the deprecated inet_ntoa call support. (David Carlier) diff --git a/UPGRADING b/UPGRADING index a6835b501495f..23f67ae2c19ba 100644 --- a/UPGRADING +++ b/UPGRADING @@ -611,7 +611,7 @@ PHP 8.4 UPGRADE NOTES . SOCK_DCCP (NetBSD only). . TCP_SYNCNT (Linux only). . SO_EXCLBIND (Solaris/Illumos only). - . SO_NOSIGPIPE (macOs only). + . SO_NOSIGPIPE (macOs and FreeBSD). - Sodium: . SODIUM_CRYPTO_AEAD_AEGIS128L_KEYBYTES From 57cd23d6d061acc6762079543a6e378d38a24485 Mon Sep 17 00:00:00 2001 From: Pascal Christen Date: Sun, 28 Apr 2024 14:18:06 +0200 Subject: [PATCH 007/201] Adjust zlog warning and error message in kqueue (#14053) --- sapi/fpm/fpm/events/kqueue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapi/fpm/fpm/events/kqueue.c b/sapi/fpm/fpm/events/kqueue.c index 1204a75ae1a39..09f5da12799be 100644 --- a/sapi/fpm/fpm/events/kqueue.c +++ b/sapi/fpm/fpm/events/kqueue.c @@ -81,7 +81,7 @@ static int fpm_event_kqueue_init(int max) /* {{{ */ kevents = calloc(max, sizeof(struct kevent)); if (!kevents) { - zlog(ZLOG_ERROR, "epoll: unable to allocate %d events", max); + zlog(ZLOG_ERROR, "kevent: unable to allocate %d events", max); return -1; } @@ -128,7 +128,7 @@ static int fpm_event_kqueue_wait(struct fpm_event_queue_s *queue, unsigned long /* trigger error unless signal interrupt */ if (errno != EINTR) { - zlog(ZLOG_WARNING, "epoll_wait() returns %d", errno); + zlog(ZLOG_WARNING, "kevent() returns %d", errno); return -1; } } From afd91fb9ac67dcd3125d2c618fbdb22b1f09539b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 28 Apr 2024 15:45:56 +0200 Subject: [PATCH 008/201] Migrate ext/odbc resources to opaque objects (#12040) Co-authored-by: Niels Dossche <7771979+nielsdos@users.noreply.github.com> --- UPGRADING | 6 + Zend/Optimizer/zend_func_infos.h | 25 - ext/odbc/odbc.stub.php | 841 +++++++------- ext/odbc/odbc_arginfo.h | 120 +- ext/odbc/php_odbc.c | 1003 +++++++++-------- ext/odbc/php_odbc_includes.h | 23 +- ext/odbc/tests/bug78470.phpt | 3 +- ext/odbc/tests/bug78473.phpt | 6 +- ext/odbc/tests/odbc_close_001.phpt | 62 + ext/odbc/tests/odbc_close_all_001.phpt | 47 +- ext/odbc/tests/odbc_columnprivileges_001.phpt | 9 +- ext/odbc/tests/odbc_free_result_001.phpt | 30 +- .../odbc_non_persistent_connection_reuse.phpt | 69 ++ ext/odbc/tests/odbc_persistent_close_all.phpt | 69 ++ ext/odbc/tests/odbc_tables_001.phpt | 12 +- 15 files changed, 1314 insertions(+), 1011 deletions(-) create mode 100644 ext/odbc/tests/odbc_close_001.phpt create mode 100644 ext/odbc/tests/odbc_non_persistent_connection_reuse.phpt create mode 100644 ext/odbc/tests/odbc_persistent_close_all.phpt diff --git a/UPGRADING b/UPGRADING index 23f67ae2c19ba..54539145de83a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -68,6 +68,12 @@ PHP 8.4 UPGRADE NOTES - ODBC: . odbc_fetch_row() returns false when a value less than or equal to 0 is passed for parameter $row. Now, a warning is emitted in this case. + . odbc_connect() and odbc_pconnect() will now return an Odbc\Connection + object rather than a resource. Return value checks using is_resource() + should be replaced with checks for `false`. + . odbc_prepare(), odbc_exec(), and various other functions will now return + an Odbc\Result object rather than a resource. Return value checks using + is_resource() should be replaced with checks for `false`. - Opcache: . The JIT config defaults changed from opcache.jit=tracing and diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h index 76628104528fe..6f039f70865ba 100644 --- a/Zend/Optimizer/zend_func_infos.h +++ b/Zend/Optimizer/zend_func_infos.h @@ -308,31 +308,6 @@ static const func_info_t func_infos[] = { F1("mysqli_stat", MAY_BE_STRING|MAY_BE_FALSE), F1("mysqli_store_result", MAY_BE_OBJECT|MAY_BE_FALSE), F1("mysqli_use_result", MAY_BE_OBJECT|MAY_BE_FALSE), - FN("odbc_prepare", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_exec", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_connect", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_pconnect", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_tables", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_columns", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_gettypeinfo", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_primarykeys", MAY_BE_RESOURCE|MAY_BE_FALSE), -#if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) - FN("odbc_procedurecolumns", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif -#if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) - FN("odbc_procedures", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif -#if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) - FN("odbc_foreignkeys", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif - FN("odbc_specialcolumns", MAY_BE_RESOURCE|MAY_BE_FALSE), - FN("odbc_statistics", MAY_BE_RESOURCE|MAY_BE_FALSE), -#if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) - FN("odbc_tableprivileges", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif -#if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) - FN("odbc_columnprivileges", MAY_BE_RESOURCE|MAY_BE_FALSE), -#endif F1("opcache_get_status", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_FALSE), F1("opcache_get_configuration", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY|MAY_BE_FALSE), F1("openssl_x509_parse", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE), diff --git a/ext/odbc/odbc.stub.php b/ext/odbc/odbc.stub.php index 8432260ba6996..8e7a43d059dfc 100644 --- a/ext/odbc/odbc.stub.php +++ b/ext/odbc/odbc.stub.php @@ -2,520 +2,447 @@ /** @generate-class-entries */ -/** - * @var string - * @cvalue PHP_ODBC_TYPE - */ -const ODBC_TYPE = UNKNOWN; -/** - * @var int - * @cvalue PHP_ODBC_BINMODE_PASSTHRU - */ -const ODBC_BINMODE_PASSTHRU = UNKNOWN; -/** - * @var int - * @cvalue PHP_ODBC_BINMODE_RETURN - */ -const ODBC_BINMODE_RETURN = UNKNOWN; -/** - * @var int - * @cvalue PHP_ODBC_BINMODE_CONVERT - */ -const ODBC_BINMODE_CONVERT = UNKNOWN; - -/* Define Constants for options. These Constants are defined in */ - -/** - * @var int - * @cvalue SQL_ODBC_CURSORS - */ -const SQL_ODBC_CURSORS = UNKNOWN; -/** - * @var int - * @cvalue SQL_CUR_USE_DRIVER - */ -const SQL_CUR_USE_DRIVER = UNKNOWN; -/** - * @var int - * @cvalue SQL_CUR_USE_IF_NEEDED - */ -const SQL_CUR_USE_IF_NEEDED = UNKNOWN; -/** - * @var int - * @cvalue SQL_CUR_USE_ODBC - */ -const SQL_CUR_USE_ODBC = UNKNOWN; - -/** - * @var int - * @cvalue SQL_CONCURRENCY - */ -const SQL_CONCURRENCY = UNKNOWN; -/** - * @var int - * @cvalue SQL_CONCUR_READ_ONLY - */ -const SQL_CONCUR_READ_ONLY = UNKNOWN; -/** - * @var int - * @cvalue SQL_CONCUR_LOCK - */ -const SQL_CONCUR_LOCK = UNKNOWN; -/** - * @var int - * @cvalue SQL_CONCUR_ROWVER - */ -const SQL_CONCUR_ROWVER = UNKNOWN; -/** - * @var int - * @cvalue SQL_CONCUR_VALUES - */ -const SQL_CONCUR_VALUES = UNKNOWN; - -/** - * @var int - * @cvalue SQL_CURSOR_TYPE - */ -const SQL_CURSOR_TYPE = UNKNOWN; -/** - * @var int - * @cvalue SQL_CURSOR_FORWARD_ONLY - */ -const SQL_CURSOR_FORWARD_ONLY = UNKNOWN; -/** - * @var int - * @cvalue SQL_CURSOR_KEYSET_DRIVEN - */ -const SQL_CURSOR_KEYSET_DRIVEN = UNKNOWN; -/** - * @var int - * @cvalue SQL_CURSOR_DYNAMIC - */ -const SQL_CURSOR_DYNAMIC = UNKNOWN; -/** - * @var int - * @cvalue SQL_CURSOR_STATIC - */ -const SQL_CURSOR_STATIC = UNKNOWN; - -/** - * @var int - * @cvalue SQL_KEYSET_SIZE - */ -const SQL_KEYSET_SIZE = UNKNOWN; - -/* these are for the Data Source type */ - -/** - * @var int - * @cvalue SQL_FETCH_FIRST - */ -const SQL_FETCH_FIRST = UNKNOWN; -/** - * @var int - * @cvalue SQL_FETCH_NEXT - */ -const SQL_FETCH_NEXT = UNKNOWN; - -/* register the standard data types */ - -/** - * @var int - * @cvalue SQL_CHAR - */ -const SQL_CHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_VARCHAR - */ -const SQL_VARCHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_LONGVARCHAR - */ -const SQL_LONGVARCHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_DECIMAL - */ -const SQL_DECIMAL = UNKNOWN; -/** - * @var int - * @cvalue SQL_NUMERIC - */ -const SQL_NUMERIC = UNKNOWN; -/** - * @var int - * @cvalue SQL_BIT - */ -const SQL_BIT = UNKNOWN; -/** - * @var int - * @cvalue SQL_TINYINT - */ -const SQL_TINYINT = UNKNOWN; -/** - * @var int - * @cvalue SQL_SMALLINT - */ -const SQL_SMALLINT = UNKNOWN; -/** - * @var int - * @cvalue SQL_INTEGER - */ -const SQL_INTEGER = UNKNOWN; -/** - * @var int - * @cvalue SQL_BIGINT - */ -const SQL_BIGINT = UNKNOWN; -/** - * @var int - * @cvalue SQL_REAL - */ -const SQL_REAL = UNKNOWN; -/** - * @var int - * @cvalue SQL_FLOAT - */ -const SQL_FLOAT = UNKNOWN; -/** - * @var int - * @cvalue SQL_DOUBLE - */ -const SQL_DOUBLE = UNKNOWN; -/** - * @var int - * @cvalue SQL_BINARY - */ -const SQL_BINARY = UNKNOWN; -/** - * @var int - * @cvalue SQL_VARBINARY - */ -const SQL_VARBINARY = UNKNOWN; -/** - * @var int - * @cvalue SQL_LONGVARBINARY - */ -const SQL_LONGVARBINARY = UNKNOWN; -/** - * @var int - * @cvalue SQL_DATE - */ -const SQL_DATE = UNKNOWN; -/** - * @var int - * @cvalue SQL_TIME - */ -const SQL_TIME = UNKNOWN; -/** - * @var int - * @cvalue SQL_TIMESTAMP - */ -const SQL_TIMESTAMP = UNKNOWN; +namespace Odbc { + /** + * @strict-properties + * @not-serializable + */ + class Connection + { + } + + /** + * @strict-properties + * @not-serializable + */ + class Result + { + } +} + +namespace { + /** + * @var string + * @cvalue PHP_ODBC_TYPE + */ + const ODBC_TYPE = UNKNOWN; + /** + * @var int + * @cvalue PHP_ODBC_BINMODE_PASSTHRU + */ + const ODBC_BINMODE_PASSTHRU = UNKNOWN; + /** + * @var int + * @cvalue PHP_ODBC_BINMODE_RETURN + */ + const ODBC_BINMODE_RETURN = UNKNOWN; + /** + * @var int + * @cvalue PHP_ODBC_BINMODE_CONVERT + */ + const ODBC_BINMODE_CONVERT = UNKNOWN; + + /* Define Constants for options. These Constants are defined in */ + + /** + * @var int + * @cvalue SQL_ODBC_CURSORS + */ + const SQL_ODBC_CURSORS = UNKNOWN; + /** + * @var int + * @cvalue SQL_CUR_USE_DRIVER + */ + const SQL_CUR_USE_DRIVER = UNKNOWN; + /** + * @var int + * @cvalue SQL_CUR_USE_IF_NEEDED + */ + const SQL_CUR_USE_IF_NEEDED = UNKNOWN; + /** + * @var int + * @cvalue SQL_CUR_USE_ODBC + */ + const SQL_CUR_USE_ODBC = UNKNOWN; + + /** + * @var int + * @cvalue SQL_CONCURRENCY + */ + const SQL_CONCURRENCY = UNKNOWN; + /** + * @var int + * @cvalue SQL_CONCUR_READ_ONLY + */ + const SQL_CONCUR_READ_ONLY = UNKNOWN; + /** + * @var int + * @cvalue SQL_CONCUR_LOCK + */ + const SQL_CONCUR_LOCK = UNKNOWN; + /** + * @var int + * @cvalue SQL_CONCUR_ROWVER + */ + const SQL_CONCUR_ROWVER = UNKNOWN; + /** + * @var int + * @cvalue SQL_CONCUR_VALUES + */ + const SQL_CONCUR_VALUES = UNKNOWN; + + /** + * @var int + * @cvalue SQL_CURSOR_TYPE + */ + const SQL_CURSOR_TYPE = UNKNOWN; + /** + * @var int + * @cvalue SQL_CURSOR_FORWARD_ONLY + */ + const SQL_CURSOR_FORWARD_ONLY = UNKNOWN; + /** + * @var int + * @cvalue SQL_CURSOR_KEYSET_DRIVEN + */ + const SQL_CURSOR_KEYSET_DRIVEN = UNKNOWN; + /** + * @var int + * @cvalue SQL_CURSOR_DYNAMIC + */ + const SQL_CURSOR_DYNAMIC = UNKNOWN; + /** + * @var int + * @cvalue SQL_CURSOR_STATIC + */ + const SQL_CURSOR_STATIC = UNKNOWN; + + /** + * @var int + * @cvalue SQL_KEYSET_SIZE + */ + const SQL_KEYSET_SIZE = UNKNOWN; + + /* these are for the Data Source type */ + + /** + * @var int + * @cvalue SQL_FETCH_FIRST + */ + const SQL_FETCH_FIRST = UNKNOWN; + /** + * @var int + * @cvalue SQL_FETCH_NEXT + */ + const SQL_FETCH_NEXT = UNKNOWN; + + /* register the standard data types */ + + /** + * @var int + * @cvalue SQL_CHAR + */ + const SQL_CHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_VARCHAR + */ + const SQL_VARCHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_LONGVARCHAR + */ + const SQL_LONGVARCHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_DECIMAL + */ + const SQL_DECIMAL = UNKNOWN; + /** + * @var int + * @cvalue SQL_NUMERIC + */ + const SQL_NUMERIC = UNKNOWN; + /** + * @var int + * @cvalue SQL_BIT + */ + const SQL_BIT = UNKNOWN; + /** + * @var int + * @cvalue SQL_TINYINT + */ + const SQL_TINYINT = UNKNOWN; + /** + * @var int + * @cvalue SQL_SMALLINT + */ + const SQL_SMALLINT = UNKNOWN; + /** + * @var int + * @cvalue SQL_INTEGER + */ + const SQL_INTEGER = UNKNOWN; + /** + * @var int + * @cvalue SQL_BIGINT + */ + const SQL_BIGINT = UNKNOWN; + /** + * @var int + * @cvalue SQL_REAL + */ + const SQL_REAL = UNKNOWN; + /** + * @var int + * @cvalue SQL_FLOAT + */ + const SQL_FLOAT = UNKNOWN; + /** + * @var int + * @cvalue SQL_DOUBLE + */ + const SQL_DOUBLE = UNKNOWN; + /** + * @var int + * @cvalue SQL_BINARY + */ + const SQL_BINARY = UNKNOWN; + /** + * @var int + * @cvalue SQL_VARBINARY + */ + const SQL_VARBINARY = UNKNOWN; + /** + * @var int + * @cvalue SQL_LONGVARBINARY + */ + const SQL_LONGVARBINARY = UNKNOWN; + /** + * @var int + * @cvalue SQL_DATE + */ + const SQL_DATE = UNKNOWN; + /** + * @var int + * @cvalue SQL_TIME + */ + const SQL_TIME = UNKNOWN; + /** + * @var int + * @cvalue SQL_TIMESTAMP + */ + const SQL_TIMESTAMP = UNKNOWN; #if (defined(ODBCVER) && (ODBCVER >= 0x0300)) -/** - * @var int - * @cvalue SQL_TYPE_DATE - */ -const SQL_TYPE_DATE = UNKNOWN; -/** - * @var int - * @cvalue SQL_TYPE_TIME - */ -const SQL_TYPE_TIME = UNKNOWN; -/** - * @var int - * @cvalue SQL_TYPE_TIMESTAMP - */ -const SQL_TYPE_TIMESTAMP = UNKNOWN; -/** - * @var int - * @cvalue SQL_WCHAR - */ -const SQL_WCHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_WVARCHAR - */ -const SQL_WVARCHAR = UNKNOWN; -/** - * @var int - * @cvalue SQL_WLONGVARCHAR - */ -const SQL_WLONGVARCHAR = UNKNOWN; - -/* SQLSpecialColumns values */ - -/** - * @var int - * @cvalue SQL_BEST_ROWID - */ -const SQL_BEST_ROWID = UNKNOWN; -/** - * @var int - * @cvalue SQL_ROWVER - */ -const SQL_ROWVER = UNKNOWN; -/** - * @var int - * @cvalue SQL_SCOPE_CURROW - */ -const SQL_SCOPE_CURROW = UNKNOWN; -/** - * @var int - * @cvalue SQL_SCOPE_TRANSACTION - */ -const SQL_SCOPE_TRANSACTION = UNKNOWN; -/** - * @var int - * @cvalue SQL_SCOPE_SESSION - */ -const SQL_SCOPE_SESSION = UNKNOWN; -/** - * @var int - * @cvalue SQL_NO_NULLS - */ -const SQL_NO_NULLS = UNKNOWN; -/** - * @var int - * @cvalue SQL_NULLABLE - */ -const SQL_NULLABLE = UNKNOWN; - -/* SQLStatistics values */ - -/** - * @var int - * @cvalue SQL_INDEX_UNIQUE - */ -const SQL_INDEX_UNIQUE = UNKNOWN; -/** - * @var int - * @cvalue SQL_INDEX_ALL - */ -const SQL_INDEX_ALL = UNKNOWN; -/** - * @var int - * @cvalue SQL_ENSURE - */ -const SQL_ENSURE = UNKNOWN; -/** - * @var int - * @cvalue SQL_QUICK - */ -const SQL_QUICK = UNKNOWN; + /** + * @var int + * @cvalue SQL_TYPE_DATE + */ + const SQL_TYPE_DATE = UNKNOWN; + /** + * @var int + * @cvalue SQL_TYPE_TIME + */ + const SQL_TYPE_TIME = UNKNOWN; + /** + * @var int + * @cvalue SQL_TYPE_TIMESTAMP + */ + const SQL_TYPE_TIMESTAMP = UNKNOWN; + /** + * @var int + * @cvalue SQL_WCHAR + */ + const SQL_WCHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_WVARCHAR + */ + const SQL_WVARCHAR = UNKNOWN; + /** + * @var int + * @cvalue SQL_WLONGVARCHAR + */ + const SQL_WLONGVARCHAR = UNKNOWN; + + /* SQLSpecialColumns values */ + + /** + * @var int + * @cvalue SQL_BEST_ROWID + */ + const SQL_BEST_ROWID = UNKNOWN; + /** + * @var int + * @cvalue SQL_ROWVER + */ + const SQL_ROWVER = UNKNOWN; + /** + * @var int + * @cvalue SQL_SCOPE_CURROW + */ + const SQL_SCOPE_CURROW = UNKNOWN; + /** + * @var int + * @cvalue SQL_SCOPE_TRANSACTION + */ + const SQL_SCOPE_TRANSACTION = UNKNOWN; + /** + * @var int + * @cvalue SQL_SCOPE_SESSION + */ + const SQL_SCOPE_SESSION = UNKNOWN; + /** + * @var int + * @cvalue SQL_NO_NULLS + */ + const SQL_NO_NULLS = UNKNOWN; + /** + * @var int + * @cvalue SQL_NULLABLE + */ + const SQL_NULLABLE = UNKNOWN; + + /* SQLStatistics values */ + + /** + * @var int + * @cvalue SQL_INDEX_UNIQUE + */ + const SQL_INDEX_UNIQUE = UNKNOWN; + /** + * @var int + * @cvalue SQL_INDEX_ALL + */ + const SQL_INDEX_ALL = UNKNOWN; + /** + * @var int + * @cvalue SQL_ENSURE + */ + const SQL_ENSURE = UNKNOWN; + /** + * @var int + * @cvalue SQL_QUICK + */ + const SQL_QUICK = UNKNOWN; #endif + function odbc_close_all(): void {} + function odbc_binmode(Odbc\Result $statement, int $mode): true {} -function odbc_close_all(): void {} + function odbc_longreadlen(Odbc\Result $statement, int $length): true {} -/** @param resource $statement */ -function odbc_binmode($statement, int $mode): true {} + function odbc_prepare(Odbc\Connection $odbc, string $query): Odbc\Result|false {} -/** @param resource $statement */ -function odbc_longreadlen($statement, int $length): true {} + function odbc_execute(Odbc\Result $statement, array $params = []): bool {} -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_prepare($odbc, string $query) {} - -/** @param resource $statement */ -function odbc_execute($statement, array $params = []): bool {} - -/** @param resource $statement */ -function odbc_cursor($statement): string|false {} + function odbc_cursor(Odbc\Result $statement): string|false {} #ifdef HAVE_SQLDATASOURCES -/** @param resource $odbc */ -function odbc_data_source($odbc, int $fetch_type): array|null|false {} + function odbc_data_source(Odbc\Connection $odbc, int $fetch_type): array|null|false {} #endif -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_exec($odbc, string $query) {} + function odbc_exec(Odbc\Connection $odbc, string $query): Odbc\Result|false {} -/** - * @param resource $odbc - * @return resource|false - * @alias odbc_exec - */ -function odbc_do($odbc, string $query) {} + /** @alias odbc_exec */ + function odbc_do(Odbc\Connection $odbc, string $query): Odbc\Result|false {} #ifdef PHP_ODBC_HAVE_FETCH_HASH -/** @param resource $statement */ -function odbc_fetch_object($statement, ?int $row = null): stdClass|false {} + /** @param resource $statement */ + function odbc_fetch_object($statement, ?int $row = null): stdClass|false {} -/** @param resource $statement */ -function odbc_fetch_array($statement, ?int $row = null): array|false {} + /** @param resource $statement */ + function odbc_fetch_array($statement, ?int $row = null): array|false {} #endif -/** - * @param resource $statement - * @param array $array - */ -function odbc_fetch_into($statement, &$array, ?int $row = null): int|false {} + /** + * @param resource $statement + * @param array $array + */ + function odbc_fetch_into($statement, &$array, ?int $row = null): int|false {} -/** @param resource $statement */ -function odbc_fetch_row($statement, ?int $row = null): bool {} + function odbc_fetch_row(Odbc\Result $statement, ?int $row = null): bool {} -/** @param resource $statement */ -function odbc_result($statement, string|int $field): string|bool|null {} + function odbc_result(Odbc\Result $statement, string|int $field): string|bool|null {} -/** - * @param resource $statement - * @deprecated - */ -function odbc_result_all($statement, string $format = ""): int|false {} + /** @deprecated */ + function odbc_result_all(Odbc\Result $statement, string $format = ""): int|false {} -/** @param resource $statement */ -function odbc_free_result($statement): true {} + function odbc_free_result(Odbc\Result $statement): true {} -/** - * @return resource|false - */ -function odbc_connect(string $dsn, ?string $user = null, #[\SensitiveParameter] ?string $password = null, int $cursor_option = SQL_CUR_USE_DRIVER) {} + function odbc_connect(string $dsn, ?string $user = null, #[\SensitiveParameter] ?string $password = null, int $cursor_option = SQL_CUR_USE_DRIVER): Odbc\Connection|false {} -/** - * @return resource|false - */ -function odbc_pconnect(string $dsn, ?string $user = null, #[\SensitiveParameter] ?string $password = null, int $cursor_option = SQL_CUR_USE_DRIVER) {} + function odbc_pconnect(string $dsn, ?string $user = null, #[\SensitiveParameter] ?string $password = null, int $cursor_option = SQL_CUR_USE_DRIVER): Odbc\Connection|false {} -/** @param resource $odbc */ -function odbc_close($odbc): void {} + function odbc_close(Odbc\Connection $odbc): void {} -/** @param resource $statement */ -function odbc_num_rows($statement): int {} + function odbc_num_rows(Odbc\Result $statement): int {} #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) -/** @param resource $statement */ -function odbc_next_result($statement): bool {} + function odbc_next_result(Odbc\Result $statement): bool {} #endif -/** @param resource $statement */ -function odbc_num_fields($statement): int {} + function odbc_num_fields(Odbc\Result $statement): int {} -/** @param resource $statement */ -function odbc_field_name($statement, int $field): string|false {} + function odbc_field_name(Odbc\Result $statement, int $field): string|false {} -/** @param resource $statement */ -function odbc_field_type($statement, int $field): string|false {} + function odbc_field_type(Odbc\Result $statement, int $field): string|false {} -/** @param resource $statement */ -function odbc_field_len($statement, int $field): int|false {} + function odbc_field_len(Odbc\Result $statement, int $field): int|false {} -/** - * @param resource $statement - * @alias odbc_field_len - */ -function odbc_field_precision($statement, int $field): int|false {} + /** @alias odbc_field_len */ + function odbc_field_precision(Odbc\Result $statement, int $field): int|false {} -/** @param resource $statement */ -function odbc_field_scale($statement, int $field): int|false {} + function odbc_field_scale(Odbc\Result $statement, int $field): int|false {} -/** @param resource $statement */ -function odbc_field_num($statement, string $field): int|false {} + function odbc_field_num(Odbc\Result $statement, string $field): int|false {} -/** @param resource $odbc */ -function odbc_autocommit($odbc, ?bool $enable = null): int|bool {} + function odbc_autocommit(Odbc\Connection $odbc, ?bool $enable = null): int|bool {} -/** @param resource $odbc */ -function odbc_commit($odbc): bool {} + function odbc_commit(Odbc\Connection $odbc): bool {} -/** @param resource $odbc */ -function odbc_rollback($odbc): bool {} + function odbc_rollback(Odbc\Connection $odbc): bool {} -/** @param resource|null $odbc */ -function odbc_error($odbc = null): string {} + function odbc_error(?Odbc\Connection $odbc = null): string {} -/** @param resource|null $odbc */ -function odbc_errormsg($odbc = null): string {} + function odbc_errormsg(?Odbc\Connection $odbc = null): string {} -/** @param resource $odbc */ -function odbc_setoption($odbc, int $which, int $option, int $value): bool {} + function odbc_setoption(Odbc\Connection|Odbc\Result $odbc, int $which, int $option, int $value): bool {} -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_tables($odbc, ?string $catalog = null, ?string $schema = null, ?string $table = null, ?string $types = null) {} + function odbc_tables(Odbc\Connection $odbc, ?string $catalog = null, ?string $schema = null, ?string $table = null, ?string $types = null): Odbc\Result|false {} -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_columns($odbc, ?string $catalog = null, ?string $schema = null, ?string $table = null, ?string $column = null) {} + function odbc_columns(Odbc\Connection $odbc, ?string $catalog = null, ?string $schema = null, ?string $table = null, ?string $column = null): Odbc\Result|false {} -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_gettypeinfo($odbc, int $data_type = 0) {} + function odbc_gettypeinfo(Odbc\Connection $odbc, int $data_type = 0): Odbc\Result|false {} -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_primarykeys($odbc, ?string $catalog, string $schema, string $table) {} + function odbc_primarykeys(Odbc\Connection $odbc, ?string $catalog, string $schema, string $table): Odbc\Result|false {} #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_procedurecolumns($odbc, ?string $catalog = null, ?string $schema = null, ?string $procedure = null, ?string $column = null) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_procedures($odbc, ?string $catalog = null, ?string $schema = null, ?string $procedure = null) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_foreignkeys($odbc, ?string $pk_catalog, string $pk_schema, string $pk_table, string $fk_catalog, string $fk_schema, string $fk_table) {} + function odbc_procedurecolumns(Odbc\Connection $odbc, ?string $catalog = null, ?string $schema = null, ?string $procedure = null, ?string $column = null): Odbc\Result|false {} + + function odbc_procedures(Odbc\Connection $odbc, ?string $catalog = null, ?string $schema = null, ?string $procedure = null): Odbc\Result|false {} + + function odbc_foreignkeys(Odbc\Connection $odbc, ?string $pk_catalog, string $pk_schema, string $pk_table, string $fk_catalog, string $fk_schema, string $fk_table): Odbc\Result|false {} #endif -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_specialcolumns($odbc, int $type, ?string $catalog, string $schema, string $table, int $scope, int $nullable) {} + function odbc_specialcolumns(Odbc\Connection $odbc, int $type, ?string $catalog, string $schema, string $table, int $scope, int $nullable): Odbc\Result|false {} -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_statistics($odbc, ?string $catalog, string $schema, string $table, int $unique, int $accuracy) {} + function odbc_statistics(Odbc\Connection $odbc, ?string $catalog, string $schema, string $table, int $unique, int $accuracy): Odbc\Result|false {} #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_tableprivileges($odbc, ?string $catalog, string $schema, string $table) {} - -/** - * @param resource $odbc - * @return resource|false - */ -function odbc_columnprivileges($odbc, ?string $catalog, string $schema, string $table, string $column) {} + function odbc_tableprivileges(Odbc\Connection $odbc, ?string $catalog, string $schema, string $table): Odbc\Result|false {} + + function odbc_columnprivileges(Odbc\Connection $odbc, ?string $catalog, string $schema, string $table, string $column): Odbc\Result|false {} #endif -/* odbc_utils.c */ + /* odbc_utils.c */ -function odbc_connection_string_is_quoted(string $str): bool {} + function odbc_connection_string_is_quoted(string $str): bool {} -function odbc_connection_string_should_quote(string $str): bool {} + function odbc_connection_string_should_quote(string $str): bool {} -function odbc_connection_string_quote(string $str): string {} + function odbc_connection_string_quote(string $str): string {} +} diff --git a/ext/odbc/odbc_arginfo.h b/ext/odbc/odbc_arginfo.h index 7a2916054152f..681625f3eb997 100644 --- a/ext/odbc/odbc_arginfo.h +++ b/ext/odbc/odbc_arginfo.h @@ -1,36 +1,36 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: a64be64f69159d0c8ad2c3b951c6451a040c3c73 */ + * Stub hash: 34cebf41d91e4dacb8655a935c629ac62f0bb5ab */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_close_all, 0, 0, IS_VOID, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_binmode, 0, 2, IS_TRUE, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_INFO(0, mode, IS_LONG, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_longreadlen, 0, 2, IS_TRUE, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_INFO(0, length, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_prepare, 0, 0, 2) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_prepare, 0, 2, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO(0, query, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_execute, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, params, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_cursor, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_END_ARG_INFO() #if defined(HAVE_SQLDATASOURCES) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_data_source, 0, 2, MAY_BE_ARRAY|MAY_BE_NULL|MAY_BE_FALSE) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO(0, fetch_type, IS_LONG, 0) ZEND_END_ARG_INFO() #endif @@ -60,25 +60,25 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_fetch_into, 0, 2, MAY_BE_LO ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_fetch_row, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, row, IS_LONG, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_result, 0, 2, MAY_BE_STRING|MAY_BE_BOOL|MAY_BE_NULL) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_MASK(0, field, MAY_BE_STRING|MAY_BE_LONG, NULL) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_result_all, 0, 1, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, format, IS_STRING, 0, "\"\"") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_free_result, 0, 1, IS_TRUE, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_connect, 0, 0, 1) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_connect, 0, 1, Odbc\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, dsn, IS_STRING, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, user, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, password, IS_STRING, 1, "null") @@ -88,30 +88,30 @@ ZEND_END_ARG_INFO() #define arginfo_odbc_pconnect arginfo_odbc_connect ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_close, 0, 1, IS_VOID, 0) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_num_rows, 0, 1, IS_LONG, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_END_ARG_INFO() #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_next_result, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_END_ARG_INFO() #endif #define arginfo_odbc_num_fields arginfo_odbc_num_rows ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_field_name, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_INFO(0, field, IS_LONG, 0) ZEND_END_ARG_INFO() #define arginfo_odbc_field_type arginfo_odbc_field_name ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_field_len, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_INFO(0, field, IS_LONG, 0) ZEND_END_ARG_INFO() @@ -120,65 +120,65 @@ ZEND_END_ARG_INFO() #define arginfo_odbc_field_scale arginfo_odbc_field_len ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_field_num, 0, 2, MAY_BE_LONG|MAY_BE_FALSE) - ZEND_ARG_INFO(0, statement) + ZEND_ARG_OBJ_INFO(0, statement, Odbc\\Result, 0) ZEND_ARG_TYPE_INFO(0, field, IS_STRING, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_odbc_autocommit, 0, 1, MAY_BE_LONG|MAY_BE_BOOL) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, enable, _IS_BOOL, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_commit, 0, 1, _IS_BOOL, 0) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_END_ARG_INFO() #define arginfo_odbc_rollback arginfo_odbc_commit ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_error, 0, 0, IS_STRING, 0) - ZEND_ARG_INFO_WITH_DEFAULT_VALUE(0, odbc, "null") + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, odbc, Odbc\\Connection, 1, "null") ZEND_END_ARG_INFO() #define arginfo_odbc_errormsg arginfo_odbc_error ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_odbc_setoption, 0, 4, _IS_BOOL, 0) - ZEND_ARG_INFO(0, odbc) + ZEND_ARG_OBJ_TYPE_MASK(0, odbc, Odbc\\Connection|Odbc\\Result, 0, NULL) ZEND_ARG_TYPE_INFO(0, which, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, option, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, value, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_tables, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_tables, 0, 1, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, catalog, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, schema, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, table, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, types, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_columns, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_columns, 0, 1, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, catalog, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, schema, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, table, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, column, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_gettypeinfo, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_gettypeinfo, 0, 1, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, data_type, IS_LONG, 0, "0") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_primarykeys, 0, 0, 4) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_primarykeys, 0, 4, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, table, IS_STRING, 0) ZEND_END_ARG_INFO() #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_procedurecolumns, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_procedurecolumns, 0, 1, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, catalog, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, schema, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, procedure, IS_STRING, 1, "null") @@ -187,8 +187,8 @@ ZEND_END_ARG_INFO() #endif #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_procedures, 0, 0, 1) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_procedures, 0, 1, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, catalog, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, schema, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, procedure, IS_STRING, 1, "null") @@ -196,8 +196,8 @@ ZEND_END_ARG_INFO() #endif #if !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) && !defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_foreignkeys, 0, 0, 7) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_foreignkeys, 0, 7, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO(0, pk_catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, pk_schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, pk_table, IS_STRING, 0) @@ -207,8 +207,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_foreignkeys, 0, 0, 7) ZEND_END_ARG_INFO() #endif -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_specialcolumns, 0, 0, 7) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_specialcolumns, 0, 7, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) @@ -217,8 +217,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_specialcolumns, 0, 0, 7) ZEND_ARG_TYPE_INFO(0, nullable, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_statistics, 0, 0, 6) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_statistics, 0, 6, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, table, IS_STRING, 0) @@ -227,8 +227,8 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_statistics, 0, 0, 6) ZEND_END_ARG_INFO() #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_tableprivileges, 0, 0, 4) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_tableprivileges, 0, 4, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, table, IS_STRING, 0) @@ -236,8 +236,8 @@ ZEND_END_ARG_INFO() #endif #if !defined(HAVE_DBMAKER) && !defined(HAVE_SOLID) && !defined(HAVE_SOLID_30) &&!defined(HAVE_SOLID_35) -ZEND_BEGIN_ARG_INFO_EX(arginfo_odbc_columnprivileges, 0, 0, 5) - ZEND_ARG_INFO(0, odbc) +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_odbc_columnprivileges, 0, 5, Odbc\\Result, MAY_BE_FALSE) + ZEND_ARG_OBJ_INFO(0, odbc, Odbc\\Connection, 0) ZEND_ARG_TYPE_INFO(0, catalog, IS_STRING, 1) ZEND_ARG_TYPE_INFO(0, schema, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, table, IS_STRING, 0) @@ -390,6 +390,14 @@ static const zend_function_entry ext_functions[] = { ZEND_FE_END }; +static const zend_function_entry class_Odbc_Connection_methods[] = { + ZEND_FE_END +}; + +static const zend_function_entry class_Odbc_Result_methods[] = { + ZEND_FE_END +}; + static void register_odbc_symbols(int module_number) { REGISTER_STRING_CONSTANT("ODBC_TYPE", PHP_ODBC_TYPE, CONST_PERSISTENT); @@ -489,3 +497,25 @@ static void register_odbc_symbols(int module_number) zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "odbc_pconnect", sizeof("odbc_pconnect") - 1), 2, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); } + +static zend_class_entry *register_class_Odbc_Connection(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Odbc", "Connection", class_Odbc_Connection_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} + +static zend_class_entry *register_class_Odbc_Result(void) +{ + zend_class_entry ce, *class_entry; + + INIT_NS_CLASS_ENTRY(ce, "Odbc", "Result", class_Odbc_Result_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/odbc/php_odbc.c b/ext/odbc/php_odbc.c index 5f6f84488030f..7d6fea2966924 100644 --- a/ext/odbc/php_odbc.c +++ b/ext/odbc/php_odbc.c @@ -29,6 +29,8 @@ #include "ext/standard/info.h" #include "ext/standard/php_string.h" #include "ext/standard/php_standard.h" +#include "Zend/zend_interfaces.h" +#include "zend_smart_str.h" #include "php_odbc.h" #include "php_odbc_includes.h" @@ -49,23 +51,227 @@ #include "odbc_arginfo.h" -/* - * not defined elsewhere - */ +#define CHECK_ODBC_CONNECTION(conn) \ + if (conn == NULL) { \ + zend_throw_error(NULL, "ODBC connection has already been closed"); \ + RETURN_THROWS(); \ + } -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif +#define CHECK_ODBC_RESULT(result) \ + if (result->conn_ptr == NULL) { \ + zend_throw_error(NULL, "ODBC result has already been closed"); \ + RETURN_THROWS(); \ + } void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent); +static void safe_odbc_disconnect(void *handle); +static void close_results_with_connection(odbc_connection *conn); +static inline odbc_result *odbc_result_from_obj(zend_object *obj); + +static int le_pconn; + +static zend_class_entry *odbc_connection_ce, *odbc_result_ce; +static zend_object_handlers odbc_connection_object_handlers, odbc_result_object_handlers; + +#define Z_ODBC_LINK_P(zv) odbc_link_from_obj(Z_OBJ_P(zv)) +#define Z_ODBC_CONNECTION_P(zv) Z_ODBC_LINK_P(zv)->connection +#define Z_ODBC_RESULT_P(zv) odbc_result_from_obj(Z_OBJ_P(zv)) + +static void odbc_insert_new_result(odbc_connection *connection, zval *result) +{ + ZEND_ASSERT(Z_TYPE_P(result) == IS_OBJECT); +#if ZEND_DEBUG + ZEND_ASSERT(instanceof_function(Z_OBJCE_P(result), odbc_result_ce)); +#endif + + odbc_result *res = Z_ODBC_RESULT_P(result); + + res->index = connection->results.nNextFreeElement; + zend_hash_index_add_new(&connection->results, res->index, result); + Z_ADDREF_P(result); +} + +static inline odbc_link *odbc_link_from_obj(zend_object *obj) +{ + return (odbc_link *)((char *)(obj) - XtOffsetOf(odbc_link, std)); +} + +static int _close_pconn_with_res(zval *zv, void *p) +{ + zend_resource *le = Z_RES_P(zv); -static int le_result, le_conn, le_pconn; + if (le->ptr == p) { + return ZEND_HASH_APPLY_REMOVE; + } + + return ZEND_HASH_APPLY_KEEP; +} + +static int _close_pconn(zval *zv) +{ + zend_resource *le = Z_RES_P(zv); + if (le->type == le_pconn) { + return ZEND_HASH_APPLY_REMOVE; + } else { + return ZEND_HASH_APPLY_KEEP; + } +} + +/* disconnect, and if it fails, then issue a rollback for any pending transaction (lurcher) */ +static void safe_odbc_disconnect( void *handle ) +{ + int ret = SQLDisconnect( handle ); + + if ( ret == SQL_ERROR ) + { + SQLTransact( NULL, handle, SQL_ROLLBACK ); + SQLDisconnect( handle ); + } +} + +static void free_connection(odbc_connection *conn, bool persistent) +{ + /* If aborted via timer expiration, don't try to call any unixODBC function */ + if (!(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { + safe_odbc_disconnect(conn->hdbc); + SQLFreeConnect(conn->hdbc); + SQLFreeEnv(conn->henv); + } + + conn->hdbc = NULL; + conn->henv = NULL; + + zend_hash_destroy(&conn->results); + + pefree(conn, persistent); + + ODBCG(num_links)--; + if (persistent) { + ODBCG(num_persistent)--; + } +} + +static void odbc_link_free(odbc_link *link) +{ + ZEND_ASSERT(link->connection && "link has already been closed"); + + close_results_with_connection(link->connection); + + if (!link->persistent) { + free_connection(link->connection, link->persistent); + } + + link->connection = NULL; + + if (link->hash) { + zend_hash_del(&ODBCG(connections), link->hash); + zend_string_release_ex(link->hash, link->persistent); + link->hash = NULL; + } +} + +static zend_object *odbc_connection_create_object(zend_class_entry *class_type) +{ + odbc_link *intern = zend_object_alloc(sizeof(odbc_link), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + + return &intern->std; +} + +static zend_function *odbc_connection_get_constructor(zend_object *object) +{ + zend_throw_error(NULL, "Cannot directly construct ODBC\\Connection, use odbc_connect() or odbc_pconnect() instead"); + return NULL; +} + +static void odbc_connection_free_obj(zend_object *obj) +{ + odbc_link *link = odbc_link_from_obj(obj); + + if (link->connection) { + odbc_link_free(link); + } + + zend_object_std_dtor(&link->std); +} + +static inline odbc_result *odbc_result_from_obj(zend_object *obj) +{ + return (odbc_result *)((char *)(obj) - XtOffsetOf(odbc_result, std)); +} + +static zend_object *odbc_result_create_object(zend_class_entry *class_type) +{ + odbc_result *intern = zend_object_alloc(sizeof(odbc_result), class_type); + + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + + return &intern->std; +} + +static zend_function *odbc_result_get_constructor(zend_object *object) +{ + zend_throw_error(NULL, "Cannot directly construct ODBC\\Result, use an appropriate odbc_* function instead"); + return NULL; +} + +static void odbc_result_free(odbc_result *res) +{ + ZEND_ASSERT(res->conn_ptr && "result has already been closed"); + + if (res->values) { + for (int i = 0; i < res->numcols; i++) { + if (res->values[i].value) { + efree(res->values[i].value); + } + } + efree(res->values); + res->values = NULL; + res->numcols = 0; + } + + /* If aborted via timer expiration, don't try to call any unixODBC function */ + if (res->stmt && !(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { +#if defined(HAVE_SOLID) || defined(HAVE_SOLID_30) || defined(HAVE_SOLID_35) + SQLTransact(res->conn_ptr->henv, res->conn_ptr->hdbc, + (SQLUSMALLINT) SQL_COMMIT); +#endif + SQLFreeStmt(res->stmt,SQL_DROP); + /* We don't want the connection to be closed after the last statement has been closed + * Connections will be closed on shutdown + */ + res->stmt = NULL; + } + if (res->param_info) { + efree(res->param_info); + res->param_info = NULL; + } + + HashTable *results = &res->conn_ptr->results; + res->conn_ptr = NULL; + zend_result status = zend_hash_index_del(results, res->index); + ZEND_ASSERT(status == SUCCESS); +} + +static void odbc_result_free_obj(zend_object *obj) +{ + odbc_result *result = odbc_result_from_obj(obj); + + if (result->conn_ptr) { + odbc_result_free(result); + } + + zend_object_std_dtor(&result->std); +} #define SAFE_SQL_NTS(n) ((SQLSMALLINT) ((n)?(SQL_NTS):0)) PHP_ODBC_API ZEND_DECLARE_MODULE_GLOBALS(odbc) static PHP_GINIT_FUNCTION(odbc); +static PHP_GSHUTDOWN_FUNCTION(odbc); /* {{{ odbc_module_entry */ zend_module_entry odbc_module_entry = { @@ -80,7 +286,7 @@ zend_module_entry odbc_module_entry = { PHP_ODBC_VERSION, PHP_MODULE_GLOBALS(odbc), PHP_GINIT(odbc), - NULL, + PHP_GSHUTDOWN(odbc), NULL, STANDARD_MODULE_PROPERTIES_EX }; @@ -93,111 +299,29 @@ ZEND_TSRMLS_CACHE_DEFINE() ZEND_GET_MODULE(odbc) #endif -/* {{{ _free_odbc_result */ -static void _free_odbc_result(zend_resource *rsrc) -{ - odbc_result *res = (odbc_result *)rsrc->ptr; - int i; - - if (res) { - if (res->values) { - for(i = 0; i < res->numcols; i++) { - if (res->values[i].value) - efree(res->values[i].value); - } - efree(res->values); - res->values = NULL; - } - /* If aborted via timer expiration, don't try to call any unixODBC function */ - if (res->stmt && !(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { -#if defined(HAVE_SOLID) || defined(HAVE_SOLID_30) || defined(HAVE_SOLID_35) - SQLTransact(res->conn_ptr->henv, res->conn_ptr->hdbc, - (SQLUSMALLINT) SQL_COMMIT); -#endif - SQLFreeStmt(res->stmt,SQL_DROP); - /* We don't want the connection to be closed after the last statement has been closed - * Connections will be closed on shutdown - * zend_list_delete(res->conn_ptr->id); - */ - } - if (res->param_info) { - efree(res->param_info); - } - efree(res); - } -} -/* }}} */ - -/* {{{ safe_odbc_disconnect - * disconnect, and if it fails, then issue a rollback for any pending transaction (lurcher) - */ -static void safe_odbc_disconnect( void *handle ) +static void close_results_with_connection(odbc_connection *conn) { - int ret; + zval *p; - ret = SQLDisconnect( handle ); - if ( ret == SQL_ERROR ) - { - SQLTransact( NULL, handle, SQL_ROLLBACK ); - SQLDisconnect( handle ); - } -} -/* }}} */ - -/* {{{ _close_odbc_conn */ -static void _close_odbc_conn(zend_resource *rsrc) -{ - zend_resource *p; - odbc_result *res; - - odbc_connection *conn = (odbc_connection *)rsrc->ptr; - - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr && (p->type == le_result)) { - res = (odbc_result *)p->ptr; - if (res->conn_ptr == conn) { - zend_list_close(p); - } + ZEND_HASH_FOREACH_VAL(&conn->results, p) { + odbc_result *result = Z_ODBC_RESULT_P(p); + if (result->conn_ptr) { + odbc_result_free(result); } } ZEND_HASH_FOREACH_END(); - /* If aborted via timer expiration, don't try to call any unixODBC function */ - if (!(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { - safe_odbc_disconnect(conn->hdbc); - SQLFreeConnect(conn->hdbc); - SQLFreeEnv(conn->henv); - } - efree(conn); - ODBCG(num_links)--; + zend_hash_clean(&conn->results); } -/* }}} */ /* {{{ void _close_odbc_pconn */ static void _close_odbc_pconn(zend_resource *rsrc) { - zend_resource *p; - odbc_result *res; odbc_connection *conn = (odbc_connection *)rsrc->ptr; - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr && (p->type == le_result)) { - res = (odbc_result *)p->ptr; - if (res->conn_ptr == conn) { - zend_list_close(p); - } - } - } ZEND_HASH_FOREACH_END(); - - /* If aborted via timer expiration, don't try to call any unixODBC function */ - if (!(PG(connection_status) & PHP_CONNECTION_TIMEOUT)) { - safe_odbc_disconnect(conn->hdbc); - SQLFreeConnect(conn->hdbc); - SQLFreeEnv(conn->henv); - } - free(conn); + close_results_with_connection(conn); + free_connection(conn, true); - ODBCG(num_links)--; - ODBCG(num_persistent)--; + rsrc->ptr = NULL; } /* }}} */ @@ -378,6 +502,12 @@ static PHP_GINIT_FUNCTION(odbc) ZEND_TSRMLS_CACHE_UPDATE(); #endif odbc_globals->num_persistent = 0; + zend_hash_init(&odbc_globals->connections, 0, NULL, NULL, true); +} + +static PHP_GSHUTDOWN_FUNCTION(odbc) +{ + zend_hash_destroy(&odbc_globals->connections); } /* {{{ PHP_MINIT_FUNCTION */ @@ -389,19 +519,39 @@ PHP_MINIT_FUNCTION(odbc) #endif REGISTER_INI_ENTRIES(); - le_result = zend_register_list_destructors_ex(_free_odbc_result, NULL, "odbc result", module_number); - le_conn = zend_register_list_destructors_ex(_close_odbc_conn, NULL, "odbc link", module_number); le_pconn = zend_register_list_destructors_ex(NULL, _close_odbc_pconn, "odbc link persistent", module_number); odbc_module_entry.type = type; + register_odbc_symbols(module_number); + + odbc_connection_ce = register_class_Odbc_Connection(); + odbc_connection_ce->create_object = odbc_connection_create_object; + odbc_connection_ce->default_object_handlers = &odbc_connection_object_handlers; + + memcpy(&odbc_connection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + odbc_connection_object_handlers.offset = XtOffsetOf(odbc_link, std); + odbc_connection_object_handlers.free_obj = odbc_connection_free_obj; + odbc_connection_object_handlers.get_constructor = odbc_connection_get_constructor; + odbc_connection_object_handlers.clone_obj = NULL; + odbc_connection_object_handlers.compare = zend_objects_not_comparable; + + odbc_result_ce = register_class_Odbc_Result(); + odbc_result_ce->create_object = odbc_result_create_object; + odbc_result_ce->default_object_handlers = &odbc_result_object_handlers; + + memcpy(&odbc_result_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + odbc_result_object_handlers.offset = XtOffsetOf(odbc_result, std); + odbc_result_object_handlers.free_obj = odbc_result_free_obj; + odbc_result_object_handlers.get_constructor = odbc_result_get_constructor; + odbc_result_object_handlers.clone_obj = NULL; + odbc_result_object_handlers.compare = zend_objects_not_comparable; + #if defined(HAVE_IBMDB2) && defined(_AIX) /* atexit() handler in the DB2/AIX library segfaults in PHP CLI */ /* DB2NOEXITLIST env variable prevents DB2 from invoking atexit() */ putenv("DB2NOEXITLIST=TRUE"); #endif - register_odbc_symbols(module_number); - return SUCCESS; } /* }}} */ @@ -510,13 +660,12 @@ void php_odbc_fetch_attribs(INTERNAL_FUNCTION_PARAMETERS, int mode) zval *pv_res; zend_long flag; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &pv_res, &flag) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &flag) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (mode) { result->longreadlen = flag; @@ -651,17 +800,15 @@ void odbc_bindcols(odbc_result *result) /* {{{ odbc_transact */ void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type) { - odbc_connection *conn; RETCODE rc; zval *pv_conn; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_conn) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); rc = SQLTransact(conn->henv, conn->hdbc, (SQLUSMALLINT)((type)?SQL_COMMIT:SQL_ROLLBACK)); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { @@ -673,19 +820,6 @@ void odbc_transact(INTERNAL_FUNCTION_PARAMETERS, int type) } /* }}} */ -/* {{{ _close_pconn_with_res */ -static int _close_pconn_with_res(zval *zv, void *p) -{ - zend_resource *le = Z_RES_P(zv); - zend_resource *res = (zend_resource*)p; - if (le->type == le_pconn && (((odbc_connection *)(le->ptr))->res == res)) { - return ZEND_HASH_APPLY_REMOVE; - } else { - return ZEND_HASH_APPLY_KEEP; - } -} -/* }}} */ - /* {{{ odbc_column_lengths */ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) { @@ -704,13 +838,12 @@ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &pv_res, &pv_num) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (pv_num < 1) { zend_argument_value_error(2, "must be greater than 0"); @@ -738,32 +871,23 @@ void odbc_column_lengths(INTERNAL_FUNCTION_PARAMETERS, int type) /* {{{ Close all ODBC connections */ PHP_FUNCTION(odbc_close_all) { - zend_resource *p; + zval *zv; if (zend_parse_parameters_none() == FAILURE) { RETURN_THROWS(); } - /* Loop through list and close all statements */ - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr && (p->type == le_result)) { - zend_list_close(p); + /* Loop through the link list, now close all links and their results */ + ZEND_HASH_FOREACH_VAL(&ODBCG(connections), zv) { + odbc_link *link = Z_ODBC_LINK_P(zv); + if (link->connection) { + odbc_link_free(link); } } ZEND_HASH_FOREACH_END(); - /* Second loop through list, now close all connections */ - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr) { - if (p->type == le_conn){ - zend_list_close(p); - } else if (p->type == le_pconn){ - zend_list_close(p); - /* Delete the persistent connection */ - zend_hash_apply_with_argument(&EG(persistent_list), - _close_pconn_with_res, (void *)p); - } - } - } ZEND_HASH_FOREACH_END(); + zend_hash_clean(&ODBCG(connections)); + + zend_hash_apply(&EG(persistent_list), _close_pconn); } /* }}} */ @@ -788,36 +912,35 @@ PHP_FUNCTION(odbc_prepare) char *query; size_t query_len; odbc_result *result = NULL; - odbc_connection *conn; RETCODE rc; int i; #ifdef HAVE_SQL_EXTENDED_FETCH SQLUINTEGER scrollopts; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pv_conn, &query, &query_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_conn, odbc_connection_ce, &query, &query_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); result->numparams = 0; result->param_info = NULL; rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -846,7 +969,7 @@ PHP_FUNCTION(odbc_prepare) break; default: odbc_sql_error(conn, result->stmt, "SQLPrepare"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -858,7 +981,6 @@ PHP_FUNCTION(odbc_prepare) } else { result->values = NULL; } - Z_ADDREF_P(pv_conn); result->conn_ptr = conn; result->fetched = 0; @@ -870,12 +992,12 @@ PHP_FUNCTION(odbc_prepare) odbc_sql_error(result->conn_ptr, result->stmt, "SQLDescribeParameter"); SQLFreeStmt(result->stmt, SQL_RESET_PARAMS); efree(result->param_info); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } } - RETURN_RES(zend_register_resource(result, le_result)); + odbc_insert_new_result(conn, return_value); } /* }}} */ @@ -914,13 +1036,12 @@ PHP_FUNCTION(odbc_execute) int i, ne; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|h", &pv_res, &pv_param_ht) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|h", &pv_res, odbc_result_ce, &pv_param_ht) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numparams > 0) { if ((ne = zend_hash_num_elements(pv_param_ht)) < result->numparams) { @@ -1071,13 +1192,12 @@ PHP_FUNCTION(odbc_cursor) odbc_result *result; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); rc = SQLGetInfo(result->conn_ptr->hdbc,SQL_MAX_CURSOR_NAME_LEN, (void *)&max_len,sizeof(max_len),&len); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { @@ -1125,11 +1245,10 @@ PHP_FUNCTION(odbc_data_source) zval *zv_conn; zend_long zv_fetch_type; RETCODE rc = 0; /* assume all is good */ - odbc_connection *conn; UCHAR server_name[100], desc[200]; SQLSMALLINT len1=0, len2=0, fetch_type; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &zv_conn, &zv_fetch_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &zv_conn, odbc_connection_ce, &zv_fetch_type) == FAILURE) { RETURN_THROWS(); } @@ -1140,9 +1259,8 @@ PHP_FUNCTION(odbc_data_source) RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(zv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(zv_conn); + CHECK_ODBC_CONNECTION(conn); /* now we have the "connection" lets call the DataSource object */ rc = SQLDataSources(conn->henv, @@ -1186,32 +1304,31 @@ PHP_FUNCTION(odbc_exec) char *query; size_t query_len; odbc_result *result = NULL; - odbc_connection *conn; RETCODE rc; #ifdef HAVE_SQL_EXTENDED_FETCH SQLUINTEGER scrollopts; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pv_conn, &query, &query_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_conn, odbc_connection_ce, &query, &query_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -1238,7 +1355,7 @@ PHP_FUNCTION(odbc_exec) */ odbc_sql_error(conn, result->stmt, "SQLExecDirect"); SQLFreeStmt(result->stmt, SQL_DROP); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -1250,10 +1367,10 @@ PHP_FUNCTION(odbc_exec) } else { result->values = NULL; } - Z_ADDREF_P(pv_conn); result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ @@ -1277,13 +1394,12 @@ static void php_odbc_fetch_hash(INTERNAL_FUNCTION_PARAMETERS, int result_type) SQLUSMALLINT RowStatus[1]; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l!", &pv_res, &pv_row, &pv_row_is_null) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!", &pv_res, odbc_result_ce, &pv_row, &pv_row_is_null) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); /* TODO deprecate $row argument values less than 1 after PHP 8.4 */ @@ -1437,13 +1553,12 @@ PHP_FUNCTION(odbc_fetch_into) SQLUSMALLINT RowStatus[1]; #endif /* HAVE_SQL_EXTENDED_FETCH */ - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz|l!", &pv_res, &pv_res_arr, &pv_row, &pv_row_is_null) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Oz|l!", &pv_res, odbc_result_ce, &pv_res_arr, &pv_row, &pv_row_is_null) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); /* TODO deprecate $row argument values less than 1 after PHP 8.4 */ @@ -1567,13 +1682,12 @@ PHP_FUNCTION(odbc_fetch_row) SQLUSMALLINT RowStatus[1]; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l!", &pv_res, &pv_row, &pv_row_is_null) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l!", &pv_res, odbc_result_ce, &pv_row, &pv_row_is_null) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); #ifndef HAVE_SQL_EXTENDED_FETCH if (!pv_row_is_null) { @@ -1605,6 +1719,7 @@ PHP_FUNCTION(odbc_fetch_row) if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { RETURN_FALSE; } + #ifdef HAVE_SQL_EXTENDED_FETCH if (!pv_row_is_null) { result->fetched = (SQLLEN)pv_row; @@ -1635,7 +1750,7 @@ PHP_FUNCTION(odbc_result) #endif ZEND_PARSE_PARAMETERS_START(2, 2) - Z_PARAM_RESOURCE(pv_res) + Z_PARAM_OBJECT_OF_CLASS(pv_res, odbc_result_ce) Z_PARAM_STR_OR_LONG(pv_field_str, pv_field_long) ZEND_PARSE_PARAMETERS_END(); @@ -1647,9 +1762,8 @@ PHP_FUNCTION(odbc_result) field_ind = (int) pv_field_long - 1; } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); @@ -1843,13 +1957,12 @@ PHP_FUNCTION(odbc_result_all) SQLUSMALLINT RowStatus[1]; #endif - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s", &pv_res, &pv_format, &pv_format_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s", &pv_res, odbc_result_ce, &pv_format, &pv_format_len) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); @@ -1975,27 +2088,15 @@ PHP_FUNCTION(odbc_free_result) { zval *pv_res; odbc_result *result; - int i; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); - if (result->values) { - for (i = 0; i < result->numcols; i++) { - if (result->values[i].value) { - efree(result->values[i].value); - } - } - efree(result->values); - result->values = NULL; - } - - zend_list_close(Z_RES_P(pv_res)); + odbc_result_free(result); RETURN_TRUE; } @@ -2016,18 +2117,32 @@ PHP_FUNCTION(odbc_pconnect) /* }}} */ /* {{{ odbc_sqlconnect */ -int odbc_sqlconnect(odbc_connection **conn, char *db, char *uid, char *pwd, int cur_opt, int persistent) +bool odbc_sqlconnect(zval *zv, char *db, char *uid, char *pwd, int cur_opt, bool persistent, char *hash, int hash_len) { RETCODE rc; + SQLRETURN ret; + odbc_link *link; - *conn = (odbc_connection *)pemalloc(sizeof(odbc_connection), persistent); - memset(*conn, 0, sizeof(odbc_connection)); - (*conn)->persistent = persistent; - SQLAllocEnv(&((*conn)->henv)); - SQLAllocConnect((*conn)->henv, &((*conn)->hdbc)); + object_init_ex(zv, odbc_connection_ce); + link = Z_ODBC_LINK_P(zv); + link->connection = pecalloc(1, sizeof(odbc_connection), persistent); + zend_hash_init(&link->connection->results, 0, NULL, ZVAL_PTR_DTOR, true); + link->persistent = persistent; + link->hash = zend_string_init(hash, hash_len, persistent); + ret = SQLAllocEnv(&link->connection->henv); + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { + odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLAllocEnv"); + return false; + } + + ret = SQLAllocConnect(link->connection->henv, &link->connection->hdbc); + if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) { + odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLAllocConnect"); + return false; + } #if defined(HAVE_SOLID) || defined(HAVE_SOLID_30) - SQLSetConnectOption((*conn)->hdbc, SQL_TRANSLATE_OPTION, + SQLSetConnectOption((link->connection->hdbc, SQL_TRANSLATE_OPTION, SQL_SOLID_XLATOPT_NOCNV); #endif #ifdef HAVE_OPENLINK @@ -2035,16 +2150,14 @@ int odbc_sqlconnect(odbc_connection **conn, char *db, char *uid, char *pwd, int char dsnbuf[1024]; short dsnbuflen; - rc = SQLDriverConnect((*conn)->hdbc, NULL, db, SQL_NTS, dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT); + rc = SQLDriverConnect(link->connection->hdbc, NULL, db, SQL_NTS, dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT); } #else if (cur_opt != SQL_CUR_DEFAULT) { - rc = SQLSetConnectOption((*conn)->hdbc, SQL_ODBC_CURSORS, cur_opt); + rc = SQLSetConnectOption(link->connection->hdbc, SQL_ODBC_CURSORS, cur_opt); if (rc != SQL_SUCCESS) { /* && rc != SQL_SUCCESS_WITH_INFO ? */ - odbc_sql_error(*conn, SQL_NULL_HSTMT, "SQLSetConnectOption"); - SQLFreeConnect((*conn)->hdbc); - pefree(*conn, persistent); - return FALSE; + odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLSetConnectOption"); + return false; } } /* Possible fix for bug #10250 @@ -2126,9 +2239,9 @@ int odbc_sqlconnect(odbc_connection **conn, char *db, char *uid, char *pwd, int } if (direct) { - rc = SQLDriverConnect((*conn)->hdbc, NULL, (SQLCHAR *) ldb, strlen(ldb), dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT); + rc = SQLDriverConnect(link->connection->hdbc, NULL, (SQLCHAR *) ldb, strlen(ldb), dsnbuf, sizeof(dsnbuf) - 1, &dsnbuflen, SQL_DRIVER_NOPROMPT); } else { - rc = SQLConnect((*conn)->hdbc, (SQLCHAR *) db, SQL_NTS, (SQLCHAR *) uid, SQL_NTS, (SQLCHAR *) pwd, SQL_NTS); + rc = SQLConnect(link->connection->hdbc, (SQLCHAR *) db, SQL_NTS, (SQLCHAR *) uid, SQL_NTS, (SQLCHAR *) pwd, SQL_NTS); } if (ldb) { @@ -2136,25 +2249,17 @@ int odbc_sqlconnect(odbc_connection **conn, char *db, char *uid, char *pwd, int } } #else - rc = SQLConnect((*conn)->hdbc, (SQLCHAR *) db, SQL_NTS, uid, SQL_NTS, pwd, SQL_NTS); + rc = SQLConnect(link->connection->hdbc, (SQLCHAR *) db, SQL_NTS, uid, SQL_NTS, pwd, SQL_NTS); #endif #endif if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - odbc_sql_error(*conn, SQL_NULL_HSTMT, "SQLConnect"); - SQLFreeConnect((*conn)->hdbc); - pefree((*conn), persistent); - return FALSE; + odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SQLConnect"); + return false; } -/* (*conn)->open = 1;*/ - return TRUE; + return true; } /* }}} */ -/* Persistent connections: two list-types le_pconn, le_conn and a plist - * where hashed connection info is stored together with index pointer to - * the actual link of type le_pconn in the list. Only persistent - * connections get hashed up. - */ /* {{{ odbc_do_connect */ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) { @@ -2190,17 +2295,16 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) persistent = 0; } + char *hashed_details; + size_t hashed_details_len = spprintf(&hashed_details, 0, "%d_%s_%s_%s_%s_%d", persistent, ODBC_TYPE, db, uid, pwd, cur_opt); + try_and_get_another_connection: if (persistent) { - char *hashed_details; - int hashed_len; zend_resource *le; - hashed_len = spprintf(&hashed_details, 0, "%s_%s_%s_%s_%d", ODBC_TYPE, db, uid, pwd, cur_opt); - /* the link is not in the persistent list */ - if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_len)) == NULL) { + if ((le = zend_hash_str_find_ptr(&EG(persistent_list), hashed_details, hashed_details_len)) == NULL) { if (ODBCG(max_links) != -1 && ODBCG(num_links) >= ODBCG(max_links)) { php_error_docref(NULL, E_WARNING, "Too many open links (" ZEND_LONG_FMT ")", ODBCG(num_links)); efree(hashed_details); @@ -2212,20 +2316,24 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) RETURN_FALSE; } - if (!odbc_sqlconnect(&db_conn, db, uid, pwd, cur_opt, 1)) { + if (!odbc_sqlconnect(return_value, db, uid, pwd, cur_opt, true, hashed_details, hashed_details_len)) { efree(hashed_details); + zval_ptr_dtor(return_value); RETURN_FALSE; } - if (zend_register_persistent_resource(hashed_details, hashed_len, db_conn, le_pconn) == NULL) { - free(db_conn); + db_conn = Z_ODBC_CONNECTION_P(return_value); + + if (zend_register_persistent_resource(hashed_details, hashed_details_len, db_conn, le_pconn) == NULL) { efree(hashed_details); + zval_ptr_dtor(return_value); RETURN_FALSE; } + + zend_hash_str_add_new(&ODBCG(connections), hashed_details, hashed_details_len, return_value); + ODBCG(num_persistent)++; ODBCG(num_links)++; - db_conn->res = zend_register_resource(db_conn, le_pconn); - RETVAL_RES(db_conn->res); } else { /* found connection */ ZEND_ASSERT(le->type == le_pconn); @@ -2248,7 +2356,7 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) &dead, 0, NULL); if (ret == SQL_SUCCESS && dead == SQL_CD_TRUE) { /* Bail early here, since we know it's gone */ - zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_len); + zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len); goto try_and_get_another_connection; } /* If the driver doesn't support it, or returns @@ -2260,7 +2368,7 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) d_name, sizeof(d_name), &len); if(ret != SQL_SUCCESS || len == 0) { - zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_len); + zend_hash_str_del(&EG(persistent_list), hashed_details, hashed_details_len); /* Commented out to fix a possible double closure error * when working with persistent connections as submitted by * bug #15758 @@ -2271,23 +2379,42 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) goto try_and_get_another_connection; } } + + zval *link_zval; + if ((link_zval = zend_hash_str_find(&ODBCG(connections), hashed_details, hashed_details_len)) == NULL) { + object_init_ex(return_value, odbc_connection_ce); + odbc_link *link = Z_ODBC_LINK_P(return_value); + link->connection = db_conn; + link->hash = zend_string_init(hashed_details, hashed_details_len, persistent); + link->persistent = true; + } else { + ZVAL_COPY(return_value, link_zval); + + ZEND_ASSERT(Z_ODBC_CONNECTION_P(return_value) == db_conn && "Persistent connection has changed"); + } } - efree(hashed_details); - db_conn->res = zend_register_resource(db_conn, le_pconn); - RETVAL_RES(db_conn->res); - } else { /* non persistent */ - if (ODBCG(max_links) != -1 && ODBCG(num_links) >= ODBCG(max_links)) { - php_error_docref(NULL, E_WARNING,"Too many open connections (" ZEND_LONG_FMT ")",ODBCG(num_links)); - RETURN_FALSE; - } + } else { /* non-persistent */ + zval *link_zval; + if ((link_zval = zend_hash_str_find(&ODBCG(connections), hashed_details, hashed_details_len)) == NULL) { /* non-persistent, new */ + if (ODBCG(max_links) != -1 && ODBCG(num_links) >= ODBCG(max_links)) { + php_error_docref(NULL, E_WARNING, "Too many open connections (" ZEND_LONG_FMT ")", ODBCG(num_links)); + efree(hashed_details); + RETURN_FALSE; + } - if (!odbc_sqlconnect(&db_conn, db, uid, pwd, cur_opt, 0)) { - RETURN_FALSE; + if (!odbc_sqlconnect(return_value, db, uid, pwd, cur_opt, false, hashed_details, hashed_details_len)) { + efree(hashed_details); + zval_ptr_dtor(return_value); + RETURN_FALSE; + } + ODBCG(num_links)++; + + zend_hash_str_add_new(&ODBCG(connections), hashed_details, hashed_details_len, return_value); + } else { /* non-persistent, pre-existing */ + ZVAL_COPY(return_value, link_zval); } - db_conn->res = zend_register_resource(db_conn, le_conn); - RETVAL_RES(db_conn->res); - ODBCG(num_links)++; } + efree(hashed_details); } /* }}} */ @@ -2295,36 +2422,20 @@ void odbc_do_connect(INTERNAL_FUNCTION_PARAMETERS, int persistent) PHP_FUNCTION(odbc_close) { zval *pv_conn; - zend_resource *p; - odbc_connection *conn; - odbc_result *res; - int is_pconn = 0; + odbc_link *link; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_conn) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_conn, odbc_connection_ce) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } - - if (Z_RES_P(pv_conn)->type == le_pconn) { - is_pconn = 1; - } - - ZEND_HASH_FOREACH_PTR(&EG(regular_list), p) { - if (p->ptr && (p->type == le_result)) { - res = (odbc_result *)p->ptr; - if (res->conn_ptr == conn) { - zend_list_close(p); - } - } - } ZEND_HASH_FOREACH_END(); + link = Z_ODBC_LINK_P(pv_conn); + odbc_connection *connection = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(connection); - zend_list_close(Z_RES_P(pv_conn)); + odbc_link_free(link); - if(is_pconn){ - zend_hash_apply_with_argument(&EG(persistent_list), _close_pconn_with_res, (void *) Z_RES_P(pv_conn)); + if (link->persistent) { + zend_hash_apply_with_argument(&EG(persistent_list), _close_pconn_with_res, (void *) connection); } } /* }}} */ @@ -2336,13 +2447,12 @@ PHP_FUNCTION(odbc_num_rows) SQLLEN rows; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); SQLRowCount(result->stmt, &rows); RETURN_LONG(rows); @@ -2357,13 +2467,12 @@ PHP_FUNCTION(odbc_next_result) zval *pv_res; int rc, i; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->values) { for(i = 0; i < result->numcols; i++) { @@ -2405,13 +2514,12 @@ PHP_FUNCTION(odbc_num_fields) odbc_result *result; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &pv_res) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O", &pv_res, odbc_result_ce) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); RETURN_LONG(result->numcols); } @@ -2424,13 +2532,12 @@ PHP_FUNCTION(odbc_field_name) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &pv_res, &pv_num) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (pv_num < 1) { zend_argument_value_error(2, "must be greater than 0"); @@ -2460,13 +2567,12 @@ PHP_FUNCTION(odbc_field_type) zval *pv_res; zend_long pv_num; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl", &pv_res, &pv_num) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ol", &pv_res, odbc_result_ce, &pv_num) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (pv_num < 1) { zend_argument_value_error(2, "must be greater than 0"); @@ -2510,13 +2616,12 @@ PHP_FUNCTION(odbc_field_num) odbc_result *result; zval *pv_res; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &pv_res, &fname, &fname_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os", &pv_res, odbc_result_ce, &fname, &fname_len) == FAILURE) { RETURN_THROWS(); } - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_res), "ODBC result", le_result)) == NULL) { - RETURN_THROWS(); - } + result = Z_ODBC_RESULT_P(pv_res); + CHECK_ODBC_RESULT(result); if (result->numcols == 0) { php_error_docref(NULL, E_WARNING, "No tuples available at this result index"); @@ -2541,19 +2646,17 @@ PHP_FUNCTION(odbc_field_num) /* There can be problems with pconnections!*/ PHP_FUNCTION(odbc_autocommit) { - odbc_connection *conn; RETCODE rc; zval *pv_conn; bool pv_onoff = 0; bool pv_onoff_is_null = true; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|b!", &pv_conn, &pv_onoff, &pv_onoff_is_null) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|b!", &pv_conn, odbc_connection_ce, &pv_onoff, &pv_onoff_is_null) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); if (!pv_onoff_is_null) { rc = SQLSetConnectOption(conn->hdbc, SQL_AUTOCOMMIT, pv_onoff ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); @@ -2596,14 +2699,14 @@ static void php_odbc_lasterror(INTERNAL_FUNCTION_PARAMETERS, int mode) zval *pv_handle = NULL; char *ret; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|r!", &pv_handle) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|O!", &pv_handle, odbc_connection_ce) == FAILURE) { RETURN_THROWS(); } if (pv_handle) { - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_handle), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + conn = Z_ODBC_CONNECTION_P(pv_handle); + CHECK_ODBC_CONNECTION(conn); + if (mode == 0) { ret = conn->laststate; } else { @@ -2644,36 +2747,42 @@ PHP_FUNCTION(odbc_errormsg) */ PHP_FUNCTION(odbc_setoption) { - odbc_connection *conn; + odbc_link *link; odbc_result *result; RETCODE rc; zval *pv_handle; zend_long pv_which, pv_opt, pv_val; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlll", &pv_handle, &pv_which, &pv_opt, &pv_val) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "olll", &pv_handle, &pv_which, &pv_opt, &pv_val) == FAILURE) { RETURN_THROWS(); } switch (pv_which) { case 1: /* SQLSetConnectOption */ - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_handle), "ODBC-Link", le_conn, le_pconn))) { + if (!instanceof_function(Z_OBJCE_P(pv_handle), odbc_connection_ce)) { + zend_argument_type_error(1, "must be of type ODBC\\Connection for SQLSetConnectOption()"); RETURN_THROWS(); } + link = Z_ODBC_LINK_P(pv_handle); + CHECK_ODBC_CONNECTION(link->connection); - if (conn->persistent) { + if (link->persistent) { php_error_docref(NULL, E_WARNING, "Unable to set option for persistent connection"); RETURN_FALSE; } - rc = SQLSetConnectOption(conn->hdbc, (unsigned short) pv_opt, pv_val); + rc = SQLSetConnectOption(link->connection->hdbc, (unsigned short) pv_opt, pv_val); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - odbc_sql_error(conn, SQL_NULL_HSTMT, "SetConnectOption"); + odbc_sql_error(link->connection, SQL_NULL_HSTMT, "SetConnectOption"); RETURN_FALSE; } break; case 2: /* SQLSetStmtOption */ - if ((result = (odbc_result *)zend_fetch_resource(Z_RES_P(pv_handle), "ODBC result", le_result)) == NULL) { + if (!instanceof_function(Z_OBJCE_P(pv_handle), odbc_result_ce)) { + zend_argument_type_error(1, "must be of type ODBC\\Result for SQLSetStmtOption()"); RETURN_THROWS(); } + result = Z_ODBC_RESULT_P(pv_handle); + CHECK_ODBC_RESULT(result); rc = SQLSetStmtOption(result->stmt, (unsigned short) pv_opt, pv_val); @@ -2700,32 +2809,31 @@ PHP_FUNCTION(odbc_tables) { zval *pv_conn; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema = NULL, *table = NULL, *type = NULL; size_t cat_len = 0, schema_len = 0, table_len = 0, type_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len, &type, &type_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2742,7 +2850,7 @@ PHP_FUNCTION(odbc_tables) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLTables"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2756,7 +2864,8 @@ PHP_FUNCTION(odbc_tables) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ @@ -2765,32 +2874,31 @@ PHP_FUNCTION(odbc_columns) { zval *pv_conn; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema = NULL, *table = NULL, *column = NULL; size_t cat_len = 0, schema_len = 0, table_len = 0, column_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len, &column, &column_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2809,7 +2917,7 @@ PHP_FUNCTION(odbc_columns) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLColumns"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2823,7 +2931,8 @@ PHP_FUNCTION(odbc_columns) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ @@ -2833,32 +2942,31 @@ PHP_FUNCTION(odbc_columnprivileges) { zval *pv_conn; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema, *table, *column; size_t cat_len = 0, schema_len, table_len, column_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!sss", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!sss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len, &column, &column_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2870,7 +2978,7 @@ PHP_FUNCTION(odbc_columnprivileges) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLColumnPrivileges"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2884,7 +2992,8 @@ PHP_FUNCTION(odbc_columnprivileges) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ #endif /* HAVE_DBMAKER || HAVE_SOLID*/ @@ -2895,12 +3004,11 @@ PHP_FUNCTION(odbc_foreignkeys) { zval *pv_conn; odbc_result *result = NULL; - odbc_connection *conn; char *pcat = NULL, *pschema, *ptable, *fcat, *fschema, *ftable; size_t pcat_len = 0, pschema_len, ptable_len, fcat_len, fschema_len, ftable_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!sssss", &pv_conn, &pcat, &pcat_len, &pschema, &pschema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!sssss", &pv_conn, odbc_connection_ce, &pcat, &pcat_len, &pschema, &pschema_len, &ptable, &ptable_len, &fcat, &fcat_len, &fschema, &fschema_len, &ftable, &ftable_len) == FAILURE) { RETURN_THROWS(); } @@ -2917,22 +3025,22 @@ PHP_FUNCTION(odbc_foreignkeys) EMPTY_TO_NULL(ftable); #endif - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2946,7 +3054,7 @@ PHP_FUNCTION(odbc_foreignkeys) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLForeignKeys"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -2960,7 +3068,8 @@ PHP_FUNCTION(odbc_foreignkeys) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ #endif /* HAVE_SOLID */ @@ -2971,32 +3080,31 @@ PHP_FUNCTION(odbc_gettypeinfo) zval *pv_conn; zend_long pv_data_type = SQL_ALL_TYPES; odbc_result *result = NULL; - odbc_connection *conn; RETCODE rc; SQLSMALLINT data_type; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &pv_conn, &pv_data_type) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|l", &pv_conn, odbc_connection_ce, &pv_data_type) == FAILURE) { RETURN_THROWS(); } data_type = (SQLSMALLINT) pv_data_type; - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3004,7 +3112,7 @@ PHP_FUNCTION(odbc_gettypeinfo) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLGetTypeInfo"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3018,7 +3126,8 @@ PHP_FUNCTION(odbc_gettypeinfo) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ @@ -3027,31 +3136,30 @@ PHP_FUNCTION(odbc_primarykeys) { zval *pv_conn; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema = NULL, *table = NULL; size_t cat_len = 0, schema_len, table_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ss", &pv_conn, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3062,7 +3170,7 @@ PHP_FUNCTION(odbc_primarykeys) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLPrimaryKeys"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3076,7 +3184,8 @@ PHP_FUNCTION(odbc_primarykeys) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ @@ -3086,32 +3195,31 @@ PHP_FUNCTION(odbc_procedurecolumns) { zval *pv_conn; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema = NULL, *proc = NULL, *col = NULL; size_t cat_len = 0, schema_len = 0, proc_len = 0, col_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len, &col, &col_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3123,7 +3231,7 @@ PHP_FUNCTION(odbc_procedurecolumns) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLProcedureColumns"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3137,7 +3245,8 @@ PHP_FUNCTION(odbc_procedurecolumns) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ #endif /* HAVE_SOLID */ @@ -3148,31 +3257,30 @@ PHP_FUNCTION(odbc_procedures) { zval *pv_conn; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema = NULL, *proc = NULL; size_t cat_len = 0, schema_len = 0, proc_len = 0; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|s!s!s!", &pv_conn, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "O|s!s!s!", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &proc, &proc_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3183,7 +3291,7 @@ PHP_FUNCTION(odbc_procedures) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLProcedures"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3197,7 +3305,8 @@ PHP_FUNCTION(odbc_procedures) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ #endif /* HAVE_SOLID */ @@ -3208,13 +3317,12 @@ PHP_FUNCTION(odbc_specialcolumns) zval *pv_conn; zend_long vtype, vscope, vnullable; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema = NULL, *name = NULL; size_t cat_len = 0, schema_len, name_len; SQLUSMALLINT type, scope, nullable; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rls!ssll", &pv_conn, &vtype, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Ols!ssll", &pv_conn, odbc_connection_ce, &vtype, &cat, &cat_len, &schema, &schema_len, &name, &name_len, &vscope, &vnullable) == FAILURE) { RETURN_THROWS(); } @@ -3223,22 +3331,22 @@ PHP_FUNCTION(odbc_specialcolumns) scope = (SQLUSMALLINT) vscope; nullable = (SQLUSMALLINT) vnullable; - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3251,7 +3359,7 @@ PHP_FUNCTION(odbc_specialcolumns) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLSpecialColumns"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3265,7 +3373,8 @@ PHP_FUNCTION(odbc_specialcolumns) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ @@ -3275,13 +3384,12 @@ PHP_FUNCTION(odbc_statistics) zval *pv_conn; zend_long vunique, vreserved; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema, *name; size_t cat_len = 0, schema_len, name_len; SQLUSMALLINT unique, reserved; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ssll", &pv_conn, &cat, &cat_len, &schema, &schema_len, + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ssll", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &name, &name_len, &vunique, &vreserved) == FAILURE) { RETURN_THROWS(); } @@ -3289,22 +3397,22 @@ PHP_FUNCTION(odbc_statistics) unique = (SQLUSMALLINT) vunique; reserved = (SQLUSMALLINT) vreserved; - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3317,7 +3425,7 @@ PHP_FUNCTION(odbc_statistics) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLStatistics"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3331,7 +3439,8 @@ PHP_FUNCTION(odbc_statistics) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ @@ -3341,31 +3450,30 @@ PHP_FUNCTION(odbc_tableprivileges) { zval *pv_conn; odbc_result *result = NULL; - odbc_connection *conn; char *cat = NULL, *schema = NULL, *table = NULL; size_t cat_len = 0, schema_len, table_len; RETCODE rc; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs!ss", &pv_conn, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "Os!ss", &pv_conn, odbc_connection_ce, &cat, &cat_len, &schema, &schema_len, &table, &table_len) == FAILURE) { RETURN_THROWS(); } - if (!(conn = (odbc_connection *)zend_fetch_resource2(Z_RES_P(pv_conn), "ODBC-Link", le_conn, le_pconn))) { - RETURN_THROWS(); - } + odbc_connection *conn = Z_ODBC_CONNECTION_P(pv_conn); + CHECK_ODBC_CONNECTION(conn); - result = (odbc_result *)ecalloc(1, sizeof(odbc_result)); + object_init_ex(return_value, odbc_result_ce); + result = Z_ODBC_RESULT_P(return_value); rc = PHP_ODBC_SQLALLOCSTMT(conn->hdbc, &(result->stmt)); if (rc == SQL_INVALID_HANDLE) { - efree(result); php_error_docref(NULL, E_WARNING, "SQLAllocStmt error 'Invalid Handle'"); + zval_ptr_dtor(return_value); RETURN_FALSE; } if (rc == SQL_ERROR) { odbc_sql_error(conn, SQL_NULL_HSTMT, "SQLAllocStmt"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3376,7 +3484,7 @@ PHP_FUNCTION(odbc_tableprivileges) if (rc == SQL_ERROR) { odbc_sql_error(conn, result->stmt, "SQLTablePrivileges"); - efree(result); + zval_ptr_dtor(return_value); RETURN_FALSE; } @@ -3390,7 +3498,8 @@ PHP_FUNCTION(odbc_tableprivileges) } result->conn_ptr = conn; result->fetched = 0; - RETURN_RES(zend_register_resource(result, le_result)); + + odbc_insert_new_result(conn, return_value); } /* }}} */ #endif /* HAVE_DBMAKER */ diff --git a/ext/odbc/php_odbc_includes.h b/ext/odbc/php_odbc_includes.h index 07ee99e2f0075..71daf2f2d5194 100644 --- a/ext/odbc/php_odbc_includes.h +++ b/ext/odbc/php_odbc_includes.h @@ -191,10 +191,16 @@ typedef struct odbc_connection { ODBC_SQL_CONN_T hdbc; char laststate[6]; char lasterrormsg[SQL_MAX_MESSAGE_LENGTH]; - zend_resource *res; - int persistent; + HashTable results; } odbc_connection; +typedef struct odbc_link { + odbc_connection *connection; + zend_string *hash; + bool persistent; + zend_object std; +} odbc_link; + typedef struct odbc_result_value { char name[256]; char *value; @@ -220,8 +226,10 @@ typedef struct odbc_result { zend_long longreadlen; int binmode; int fetched; - odbc_param_info * param_info; + odbc_param_info *param_info; odbc_connection *conn_ptr; + uint32_t index; + zend_object std; } odbc_result; ZEND_BEGIN_MODULE_GLOBALS(odbc) @@ -240,8 +248,13 @@ ZEND_BEGIN_MODULE_GLOBALS(odbc) zend_long default_cursortype; char laststate[6]; char lasterrormsg[SQL_MAX_MESSAGE_LENGTH]; - HashTable *resource_list; - HashTable *resource_plist; + /* Stores ODBC links throughout the duration of a request. The connection member may be either persistent or + * non-persistent. In the former case, it is a pointer to an item in EG(persistent_list). This solution makes it + * possible to properly free links during RSHUTDOWN (or when they are explicitly closed), while persistent + * connections themselves are going to be freed later during the shutdown process (or when they are explicitly + * closed). + */ + HashTable connections; ZEND_END_MODULE_GLOBALS(odbc) int odbc_add_result(HashTable *list, odbc_result *result); diff --git a/ext/odbc/tests/bug78470.phpt b/ext/odbc/tests/bug78470.phpt index ea601a1fdfcac..92bd5c067f7b0 100644 --- a/ext/odbc/tests/bug78470.phpt +++ b/ext/odbc/tests/bug78470.phpt @@ -12,4 +12,5 @@ $conn = odbc_connect($dsn, $user, $pass); var_dump(odbc_specialcolumns($conn, SQL_BEST_ROWID, '', '', '', SQL_SCOPE_CURROW, SQL_NO_NULLS)); ?> --EXPECTF-- -resource(%d) of type (odbc result) +object(Odbc\Result)#%d (%d) { +} diff --git a/ext/odbc/tests/bug78473.phpt b/ext/odbc/tests/bug78473.phpt index 3903187a36219..9313237efde2b 100644 --- a/ext/odbc/tests/bug78473.phpt +++ b/ext/odbc/tests/bug78473.phpt @@ -11,6 +11,6 @@ try { } var_dump(STDIN); ?> ---EXPECTF-- -odbc_close(): supplied resource is not a valid ODBC-Link resource -resource(%d) of type (stream) +--EXPECT-- +odbc_close(): Argument #1 ($odbc) must be of type Odbc\Connection, resource given +resource(1) of type (stream) diff --git a/ext/odbc/tests/odbc_close_001.phpt b/ext/odbc/tests/odbc_close_001.phpt new file mode 100644 index 0000000000000..1c970b58959d9 --- /dev/null +++ b/ext/odbc/tests/odbc_close_001.phpt @@ -0,0 +1,62 @@ +--TEST-- +odbc_close(): Basic test +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- +getMessage() . "\n"; +} + +try { + odbc_columns($conn2, '', '', '', ''); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + odbc_num_rows($result1); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + odbc_num_rows($result2); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +?> +--EXPECTF-- +object(Odbc\Connection)#%d (%d) { +} +object(Odbc\Connection)#%d (%d) { +} +object(Odbc\Result)#%d (%d) { +} +object(Odbc\Result)#%d (%d) { +} +ODBC connection has already been closed +ODBC connection has already been closed +ODBC result has already been closed +ODBC result has already been closed diff --git a/ext/odbc/tests/odbc_close_all_001.phpt b/ext/odbc/tests/odbc_close_all_001.phpt index f5334ce7500a2..3dc24b5679d56 100644 --- a/ext/odbc/tests/odbc_close_all_001.phpt +++ b/ext/odbc/tests/odbc_close_all_001.phpt @@ -21,18 +21,41 @@ var_dump($result2); odbc_close_all(); -var_dump($conn1); -var_dump($conn2); -var_dump($result1); -var_dump($result2); +try { + odbc_columns($conn1, '', '', '', ''); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + odbc_columns($conn2, '', '', '', ''); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + odbc_num_rows($result1); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} + +try { + odbc_num_rows($result2); +} catch (Error $e) { + echo $e->getMessage() . "\n"; +} ?> --EXPECTF-- -resource(%d) of type (odbc link) -resource(%d) of type (odbc link persistent) -resource(%d) of type (odbc result) -resource(%d) of type (odbc result) -resource(%d) of type (Unknown) -resource(%d) of type (Unknown) -resource(%d) of type (Unknown) -resource(%d) of type (Unknown) +object(Odbc\Connection)#%d (%d) { +} +object(Odbc\Connection)#%d (%d) { +} +object(Odbc\Result)#%d (%d) { +} +object(Odbc\Result)#%d (%d) { +} +ODBC connection has already been closed +ODBC connection has already been closed +ODBC result has already been closed +ODBC result has already been closed diff --git a/ext/odbc/tests/odbc_columnprivileges_001.phpt b/ext/odbc/tests/odbc_columnprivileges_001.phpt index 5aba25895551f..be8fa20699009 100644 --- a/ext/odbc/tests/odbc_columnprivileges_001.phpt +++ b/ext/odbc/tests/odbc_columnprivileges_001.phpt @@ -22,7 +22,8 @@ var_dump(odbc_fetch_row($result)); ?> --EXPECTF-- -resource(%d) of type (odbc result) +object(Odbc\Result)#%d (%d) { +} bool(false) Deprecated: odbc_columnprivileges(): Passing null to parameter #3 ($schema) of type string is deprecated in %s on line %d @@ -30,7 +31,9 @@ Deprecated: odbc_columnprivileges(): Passing null to parameter #3 ($schema) of t Deprecated: odbc_columnprivileges(): Passing null to parameter #4 ($table) of type string is deprecated in %s on line %d Deprecated: odbc_columnprivileges(): Passing null to parameter #5 ($column) of type string is deprecated in %s on line %d -resource(%d) of type (odbc result) +object(Odbc\Result)#%d (%d) { +} bool(false) -resource(%d) of type (odbc result) +object(Odbc\Result)#%d (%d) { +} bool(false) diff --git a/ext/odbc/tests/odbc_free_result_001.phpt b/ext/odbc/tests/odbc_free_result_001.phpt index 8fc4075581a2f..28808f64208cf 100644 --- a/ext/odbc/tests/odbc_free_result_001.phpt +++ b/ext/odbc/tests/odbc_free_result_001.phpt @@ -17,22 +17,34 @@ odbc_exec($conn, 'INSERT INTO free_result VALUES (1), (2)'); $res = odbc_exec($conn, 'SELECT * FROM free_result'); -var_dump(odbc_fetch_row($res)); -var_dump(odbc_result($res, 'test')); -var_dump(odbc_free_result($res)); try { - var_dump(odbc_free_result($conn)); + var_dump(odbc_fetch_row($res)); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + var_dump(odbc_result($res, 'test')); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} +try { + var_dump(odbc_free_result($res)); } catch (TypeError $e) { echo $e->getMessage(), "\n"; } +try { + var_dump(odbc_free_result($conn)); +} catch (Error $e) { + echo $e->getMessage(), "\n"; +} try { var_dump(odbc_fetch_row($res)); -} catch (TypeError $e) { +} catch (Error $e) { echo $e->getMessage(), "\n"; } try { var_dump(odbc_result($res, 'test')); -} catch (TypeError $e) { +} catch (Error $e) { echo $e->getMessage(), "\n"; } ?> @@ -46,6 +58,6 @@ odbc_exec($conn, 'DROP TABLE free_result'); bool(true) string(1) "1" bool(true) -odbc_free_result(): supplied resource is not a valid ODBC result resource -odbc_fetch_row(): supplied resource is not a valid ODBC result resource -odbc_result(): supplied resource is not a valid ODBC result resource +odbc_free_result(): Argument #1 ($statement) must be of type Odbc\Result, Odbc\Connection given +ODBC result has already been closed +ODBC result has already been closed diff --git a/ext/odbc/tests/odbc_non_persistent_connection_reuse.phpt b/ext/odbc/tests/odbc_non_persistent_connection_reuse.phpt new file mode 100644 index 0000000000000..12bbc9fc6faea --- /dev/null +++ b/ext/odbc/tests/odbc_non_persistent_connection_reuse.phpt @@ -0,0 +1,69 @@ +--TEST-- +odbc_pconnect(): Make sure non-persistent connections are reused +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(21) "PHP odbc_connect test" +string(21) "PHP odbc_connect test" +NULL diff --git a/ext/odbc/tests/odbc_persistent_close_all.phpt b/ext/odbc/tests/odbc_persistent_close_all.phpt new file mode 100644 index 0000000000000..6bc5a4563978f --- /dev/null +++ b/ext/odbc/tests/odbc_persistent_close_all.phpt @@ -0,0 +1,69 @@ +--TEST-- +odbc_pconnect(): Make sure closing a persistent connection works in case of odbc_close_all() +--EXTENSIONS-- +odbc +--SKIPIF-- + +--FILE-- + +--EXPECT-- +string(22) "PHP odbc_pconnect test" +string(22) "PHP odbc_pconnect test" +NULL diff --git a/ext/odbc/tests/odbc_tables_001.phpt b/ext/odbc/tests/odbc_tables_001.phpt index 7d4d816c7de06..4708bcd801d6f 100644 --- a/ext/odbc/tests/odbc_tables_001.phpt +++ b/ext/odbc/tests/odbc_tables_001.phpt @@ -27,13 +27,17 @@ var_dump(odbc_fetch_row($result)); ?> --EXPECTF-- -resource(%d) of type (odbc result) +object(Odbc\Result)#%d (%d) { +} bool(false) -resource(%d) of type (odbc result) +object(Odbc\Result)#%d (%d) { +} bool(true) bool(true) -resource(%d) of type (odbc result) +object(Odbc\Result)#%d (%d) { +} bool(true) bool(true) -resource(%d) of type (odbc result) +object(Odbc\Result)#%d (%d) { +} bool(false) From 956c3c2c034bd2146dc29ec04205cb2de984a5c2 Mon Sep 17 00:00:00 2001 From: Adam Saponara Date: Fri, 12 Apr 2024 12:52:04 -0400 Subject: [PATCH 009/201] ext/openssl: Add option to load legacy algorithm provider OpenSSL 3.x relegated a set of insecure algorithms to a "legacy" provider which is not loaded by default. Some of these algorithms have utility beyond encryption such as for hashing, e.g., DES[1] Add a compile-time option to load the legacy provider in 3.x. When enabled, also load the default provider because loading any provider explicitly disables auto-loading the default provider. [1] https://github.com/vitessio/vitess/blob/9e40015748ede158357bd7291f583db138abc3df/go/vt/vtgate/vindexes/hash.go#L157 Closes GH-13951 --- NEWS | 2 ++ ext/openssl/config0.m4 | 11 +++++++++++ ext/openssl/openssl.c | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/NEWS b/NEWS index c47383edb5463..86af67c92d7ef 100644 --- a/NEWS +++ b/NEWS @@ -123,6 +123,8 @@ PHP NEWS . Added X509_PURPOSE_OCSP_HELPER and X509_PURPOSE_TIMESTAMP_SIGN constants. (Vincent Jardin) . Bumped minimum required OpenSSL version to 1.1.1. (Ayesh Karunaratne) + . Added compile-time option --with-openssl-legacy-provider to enable legacy + provider. (Adam Saponara) - Output: . Clear output handler status flags during handler initialization. (haszi) diff --git a/ext/openssl/config0.m4 b/ext/openssl/config0.m4 index 1861a09ca5496..a2404c601cc78 100644 --- a/ext/openssl/config0.m4 +++ b/ext/openssl/config0.m4 @@ -10,6 +10,13 @@ PHP_ARG_WITH([system-ciphers], [no], [no]) +PHP_ARG_WITH([openssl-legacy-provider], + [whether to load legacy algorithm provider], + [AS_HELP_STRING([--with-openssl-legacy-provider], + [OPENSSL: Load legacy algorithm provider in addition to default provider])], + [no], + [no]) + if test "$PHP_OPENSSL" != "no"; then PHP_NEW_EXTENSION(openssl, openssl.c xp_ssl.c, $ext_shared) PHP_SUBST(OPENSSL_SHARED_LIBADD) @@ -25,4 +32,8 @@ if test "$PHP_OPENSSL" != "no"; then if test "$PHP_SYSTEM_CIPHERS" != "no"; then AC_DEFINE(USE_OPENSSL_SYSTEM_CIPHERS,1,[ Use system default cipher list instead of hardcoded value ]) fi + + if test "$PHP_OPENSSL_LEGACY_PROVIDER" != "no"; then + AC_DEFINE(LOAD_OPENSSL_LEGACY_PROVIDER,1,[ Load legacy algorithm provider in addition to default provider ]) + fi fi diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 949f5d76245e8..45db5065707a7 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -59,6 +59,7 @@ #if PHP_OPENSSL_API_VERSION >= 0x30000 #include #include +#include #endif #if defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_NO_ENGINE) @@ -1277,6 +1278,10 @@ PHP_MINIT_FUNCTION(openssl) OpenSSL_add_all_algorithms(); SSL_load_error_strings(); #else +#if PHP_OPENSSL_API_VERSION >= 0x30000 && defined(LOAD_OPENSSL_LEGACY_PROVIDER) + OSSL_PROVIDER_load(NULL, "legacy"); + OSSL_PROVIDER_load(NULL, "default"); +#endif OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL); #endif From ed916214c478da36d473d425ca30e2ce7c310e87 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Mon, 29 Apr 2024 08:39:44 +0200 Subject: [PATCH 010/201] Avoid additional allocation in Document\createElementNS (#14071) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the following benchmark code: ```php $dom = DOM\XMLDocument::createEmpty(); for ($i = 0; $i < 1000*100; $i++) $dom->createElementNS("urn:a", "thisisaveryverylongname"); ``` We obtain the following on an i7-4790: ``` Benchmark 1: ./sapi/cli/php bench.php Time (mean ± σ): 34.5 ms ± 1.2 ms [User: 31.4 ms, System: 2.9 ms] Range (min … max): 32.4 ms … 39.3 ms 84 runs Benchmark 2: ./sapi/cli/php_old bench.php Time (mean ± σ): 36.6 ms ± 1.6 ms [User: 33.6 ms, System: 2.9 ms] Range (min … max): 34.3 ms … 45.3 ms 80 runs Summary ./sapi/cli/php bench.php ran 1.06 ± 0.06 times faster than ./sapi/cli/php_old bench.php ``` --- ext/dom/document.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/dom/document.c b/ext/dom/document.c index f6a778f086ddd..1eeadd15d6d78 100644 --- a/ext/dom/document.c +++ b/ext/dom/document.c @@ -913,7 +913,7 @@ PHP_METHOD(DOM_Document, createElementNS) if (errorcode == 0) { php_dom_libxml_ns_mapper *ns_mapper = php_dom_get_ns_mapper(intern); xmlNsPtr ns = php_dom_libxml_ns_mapper_get_ns_raw_prefix_string(ns_mapper, prefix, xmlStrlen(prefix), uri); - xmlNodePtr nodep = xmlNewDocNode(docp, ns, localname, NULL); + xmlNodePtr nodep = xmlNewDocNodeEatName(docp, ns, localname, NULL); if (UNEXPECTED(nodep == NULL)) { php_dom_throw_error(INVALID_STATE_ERR, /* strict */ true); } else { @@ -921,9 +921,9 @@ PHP_METHOD(DOM_Document, createElementNS) } } else { php_dom_throw_error(errorcode, dom_get_strict_error(intern->document)); + xmlFree(localname); } - xmlFree(localname); xmlFree(prefix); } /* }}} end dom_document_create_element_ns */ From 32936c518acb6c1c23367d8bd46f8f933b49d67a Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Mon, 29 Apr 2024 13:54:07 +0100 Subject: [PATCH 011/201] ext/pcntl: porting pcntl cpu affinity api to netbsd. (#14056) --- ext/pcntl/pcntl.c | 47 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/ext/pcntl/pcntl.c b/ext/pcntl/pcntl.c index 229d66c73c116..97baf1dc67e32 100644 --- a/ext/pcntl/pcntl.c +++ b/ext/pcntl/pcntl.c @@ -49,6 +49,33 @@ #include typedef cpuset_t cpu_set_t; #endif + #define PCNTL_CPUSET(mask) &mask + #define PCNTL_CPUSET_SIZE(mask) sizeof(mask) + #define PCNTL_CPU_ISSET(i, mask) CPU_ISSET(i, &mask) + #define PCNTL_CPU_SET(i, mask) CPU_SET(i, &mask) + #define PCNTL_CPU_ZERO(mask) CPU_ZERO(&mask) + #define PCNTL_CPU_DESTROY(mask) ((void)0) +#elif defined(__NetBSD__) +#include +#include +typedef cpuset_t *cpu_set_t; + #define sched_getaffinity(p, c, m) syscall(SYS__sched_getaffinity, p, 0, c, m) + #define sched_setaffinity(p, c, m) syscall(SYS__sched_setaffinity, p, 0, c, m) + #define PCNTL_CPUSET(mask) mask + #define PCNTL_CPUSET_SIZE(mask) cpuset_size(mask) + #define PCNTL_CPU_ISSET(i, mask) cpuset_isset((cpuid_t)i, mask) + #define PCNTL_CPU_SET(i, mask) cpuset_set((cpuid_t)i, mask) + #define PCNTL_CPU_ZERO(mask) \ + do { \ + mask = cpuset_create(); \ + if (UNEXPECTED(!mask)) { \ + php_error_docref(NULL, E_WARNING, "cpuset_create: Insufficient memory"); \ + RETURN_FALSE; \ + } \ + cpuset_zero(mask); \ + } while(0) + #define PCNTL_CPU_DESTROY(mask) cpuset_destroy(mask) + #define HAVE_SCHED_SETAFFINITY 1 #endif #if defined(HAVE_PTHREAD_SET_QOS_CLASS_SELF_NP) @@ -1533,9 +1560,10 @@ PHP_FUNCTION(pcntl_getcpuaffinity) // 0 == getpid in this context, we're just saving a syscall pid = pid_is_null ? 0 : pid; - CPU_ZERO(&mask); + PCNTL_CPU_ZERO(mask); - if (sched_getaffinity(pid, sizeof(mask), &mask) != 0) { + if (sched_getaffinity(pid, PCNTL_CPUSET_SIZE(mask), PCNTL_CPUSET(mask)) != 0) { + PCNTL_CPU_DESTROY(mask); PCNTL_G(last_error) = errno; switch (errno) { case ESRCH: @@ -1558,10 +1586,11 @@ PHP_FUNCTION(pcntl_getcpuaffinity) array_init(return_value); for (zend_ulong i = 0; i < maxcpus; i ++) { - if (CPU_ISSET(i, &mask)) { + if (PCNTL_CPU_ISSET(i, mask)) { add_next_index_long(return_value, i); } } + PCNTL_CPU_DESTROY(mask); } PHP_FUNCTION(pcntl_setcpuaffinity) @@ -1585,7 +1614,7 @@ PHP_FUNCTION(pcntl_setcpuaffinity) // 0 == getpid in this context, we're just saving a syscall pid = pid_is_null ? 0 : pid; zend_ulong maxcpus = (zend_ulong)sysconf(_SC_NPROCESSORS_CONF); - CPU_ZERO(&mask); + PCNTL_CPU_ZERO(mask); ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(hmask), ncpu) { ZVAL_DEREF(ncpu); @@ -1595,6 +1624,7 @@ PHP_FUNCTION(pcntl_setcpuaffinity) zend_ulong tmp; if (!ZEND_HANDLE_NUMERIC(Z_STR_P(ncpu), tmp)) { zend_argument_value_error(2, "cpu id invalid value (%s)", ZSTR_VAL(Z_STR_P(ncpu))); + PCNTL_CPU_DESTROY(mask); RETURN_THROWS(); } @@ -1603,6 +1633,7 @@ PHP_FUNCTION(pcntl_setcpuaffinity) zend_string *wcpu = zval_get_string_func(ncpu); zend_argument_value_error(2, "cpu id invalid type (%s)", ZSTR_VAL(wcpu)); zend_string_release(wcpu); + PCNTL_CPU_DESTROY(mask); RETURN_THROWS(); } } else { @@ -1614,12 +1645,13 @@ PHP_FUNCTION(pcntl_setcpuaffinity) RETURN_THROWS(); } - if (!CPU_ISSET(cpu, &mask)) { - CPU_SET(cpu, &mask); + if (!PCNTL_CPU_ISSET(cpu, mask)) { + PCNTL_CPU_SET(cpu, mask); } } ZEND_HASH_FOREACH_END(); - if (sched_setaffinity(pid, sizeof(mask), &mask) != 0) { + if (sched_setaffinity(pid, PCNTL_CPUSET_SIZE(mask), PCNTL_CPUSET(mask)) != 0) { + PCNTL_CPU_DESTROY(mask); PCNTL_G(last_error) = errno; switch (errno) { case ESRCH: @@ -1636,6 +1668,7 @@ PHP_FUNCTION(pcntl_setcpuaffinity) } RETURN_FALSE; } else { + PCNTL_CPU_DESTROY(mask); RETURN_TRUE; } } From d670e131df262db13800a026e72f7fe6fe62bcd2 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 29 Apr 2024 16:13:49 +0200 Subject: [PATCH 012/201] Fix __SANITIZE_ADDRESS__ redeclaration warning --- Zend/zend_portability.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_portability.h b/Zend/zend_portability.h index d59fc6d07c1db..6ca9b7266b6e4 100644 --- a/Zend/zend_portability.h +++ b/Zend/zend_portability.h @@ -132,7 +132,7 @@ #if defined(HAVE_LIBDL) && !defined(ZEND_WIN32) -# if __has_feature(address_sanitizer) +# if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__) # define __SANITIZE_ADDRESS__ # endif From 782af7a963e66b4da7646b4c84d820fa376d88b0 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Mon, 29 Apr 2024 16:36:24 +0200 Subject: [PATCH 013/201] Fix -Walloc-size warning It's indeed unsafe to treat zend_internal_function as zend_function, because sizeof(zend_internal_function) < sizeof(zend_function), which can lead to buffer overflows. This might also be UB. Either way, this would need to be addressed in the whole codebase. --- Zend/zend_inheritance.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 491d68714b3c2..c07415ce8204b 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -97,7 +97,7 @@ static zend_function *zend_duplicate_internal_function(zend_function *func, zend zend_function *new_function; if (UNEXPECTED(ce->type & ZEND_INTERNAL_CLASS)) { - new_function = pemalloc(sizeof(zend_internal_function), 1); + new_function = (zend_function *)pemalloc(sizeof(zend_internal_function), 1); memcpy(new_function, func, sizeof(zend_internal_function)); } else { new_function = zend_arena_alloc(&CG(arena), sizeof(zend_internal_function)); From 5823a96f1e86074626b65f8fd07576f61ee5694a Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 3 Mar 2024 11:53:49 +0000 Subject: [PATCH 014/201] ext/gettext: update arguments handling. using zend_string whenever relevant too. Close GH-13582. --- ext/gettext/gettext.c | 140 ++++++++++-------- ext/gettext/gettext.stub.php | 6 +- ext/gettext/gettext_arginfo.h | 14 +- .../tests/gettext_textdomain-retval.phpt | 7 + 4 files changed, 99 insertions(+), 68 deletions(-) diff --git a/ext/gettext/gettext.c b/ext/gettext/gettext.c index 8518a80520b55..a921070197020 100644 --- a/ext/gettext/gettext.c +++ b/ext/gettext/gettext.c @@ -81,18 +81,20 @@ PHP_MINFO_FUNCTION(php_gettext) /* {{{ Set the textdomain to "domain". Returns the current domain */ PHP_FUNCTION(textdomain) { - char *domain_name = NULL, *retval; + char *domain_name = NULL, *retval = NULL; zend_string *domain = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "S!", &domain) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(domain) + ZEND_PARSE_PARAMETERS_END(); if (domain != NULL) { PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain)) - } - - if (domain != NULL && !zend_string_equals_literal(domain, "0")) { + if (zend_string_equals_literal(domain, "0")) { + zend_argument_value_error(1, "cannot be zero"); + RETURN_THROWS(); + } domain_name = ZSTR_VAL(domain); } @@ -105,7 +107,7 @@ PHP_FUNCTION(textdomain) /* {{{ Return the translation of msgid for the current domain, or msgid unaltered if a translation does not exist */ PHP_FUNCTION(gettext) { - char *msgstr; + char *msgstr = NULL; zend_string *msgid; ZEND_PARSE_PARAMETERS_START(1, 1) @@ -126,12 +128,13 @@ PHP_FUNCTION(gettext) /* {{{ Return the translation of msgid for domain_name, or msgid unaltered if a translation does not exist */ PHP_FUNCTION(dgettext) { - char *msgstr; + char *msgstr = NULL; zend_string *domain, *msgid; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "SS", &domain, &msgid) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(domain) + Z_PARAM_STR(msgid) + ZEND_PARSE_PARAMETERS_END(); PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain)) PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid)) @@ -149,13 +152,15 @@ PHP_FUNCTION(dgettext) /* {{{ Return the translation of msgid for domain_name and category, or msgid unaltered if a translation does not exist */ PHP_FUNCTION(dcgettext) { - char *msgstr; + char *msgstr = NULL; zend_string *domain, *msgid; zend_long category; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "SSl", &domain, &msgid, &category) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(domain) + Z_PARAM_STR(msgid) + Z_PARAM_LONG(category) + ZEND_PARSE_PARAMETERS_END(); PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain)) PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid)) @@ -174,19 +179,24 @@ PHP_FUNCTION(dcgettext) /* {{{ Bind to the text domain domain_name, looking for translations in dir. Returns the current domain */ PHP_FUNCTION(bindtextdomain) { - char *domain; - size_t domain_len; - zend_string *dir = NULL; + zend_string *domain, *dir = NULL; char *retval, dir_name[MAXPATHLEN]; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sS!", &domain, &domain_len, &dir) == FAILURE) { + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(domain) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(dir) + ZEND_PARSE_PARAMETERS_END(); + + PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain)) + + if (!ZSTR_LEN(domain)) { + zend_argument_value_error(1, "cannot be empty"); RETURN_THROWS(); } - PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len) - if (dir == NULL) { - RETURN_STRING(bindtextdomain(domain, NULL)); + RETURN_STRING(bindtextdomain(ZSTR_VAL(domain), NULL)); } if (ZSTR_LEN(dir) != 0 && !zend_string_equals_literal(dir, "0")) { @@ -197,7 +207,7 @@ PHP_FUNCTION(bindtextdomain) RETURN_FALSE; } - retval = bindtextdomain(domain, dir_name); + retval = bindtextdomain(ZSTR_VAL(domain), dir_name); RETURN_STRING(retval); } @@ -207,18 +217,20 @@ PHP_FUNCTION(bindtextdomain) /* {{{ Plural version of gettext() */ PHP_FUNCTION(ngettext) { - char *msgid1, *msgid2, *msgstr; - size_t msgid1_len, msgid2_len; + char *msgstr = NULL; + zend_string *msgid1, *msgid2; zend_long count; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl", &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(3, 3) + Z_PARAM_STR(msgid1) + Z_PARAM_STR(msgid2) + Z_PARAM_LONG(count) + ZEND_PARSE_PARAMETERS_END(); - PHP_GETTEXT_LENGTH_CHECK(1, msgid1_len) - PHP_GETTEXT_LENGTH_CHECK(2, msgid2_len) + PHP_GETTEXT_LENGTH_CHECK(1, ZSTR_LEN(msgid1)) + PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid2)) - msgstr = ngettext(msgid1, msgid2, count); + msgstr = ngettext(ZSTR_VAL(msgid1), ZSTR_VAL(msgid2), count); ZEND_ASSERT(msgstr); RETURN_STRING(msgstr); @@ -230,20 +242,22 @@ PHP_FUNCTION(ngettext) /* {{{ Plural version of dgettext() */ PHP_FUNCTION(dngettext) { - char *domain, *msgid1, *msgid2, *msgstr = NULL; - size_t domain_len, msgid1_len, msgid2_len; + char *msgstr = NULL; + zend_string *domain, *msgid1, *msgid2; zend_long count; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssl", &domain, &domain_len, - &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(4, 4) + Z_PARAM_STR(domain) + Z_PARAM_STR(msgid1) + Z_PARAM_STR(msgid2) + Z_PARAM_LONG(count) + ZEND_PARSE_PARAMETERS_END(); - PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len) - PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len) - PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len) + PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain)) + PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid1)) + PHP_GETTEXT_LENGTH_CHECK(3, ZSTR_LEN(msgid2)) - msgstr = dngettext(domain, msgid1, msgid2, count); + msgstr = dngettext(ZSTR_VAL(domain), ZSTR_VAL(msgid1), ZSTR_VAL(msgid2), count); ZEND_ASSERT(msgstr); RETURN_STRING(msgstr); @@ -255,23 +269,26 @@ PHP_FUNCTION(dngettext) /* {{{ Plural version of dcgettext() */ PHP_FUNCTION(dcngettext) { - char *domain, *msgid1, *msgid2, *msgstr = NULL; - size_t domain_len, msgid1_len, msgid2_len; + char *msgstr = NULL; + zend_string *domain, *msgid1, *msgid2; zend_long count, category; RETVAL_FALSE; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "sssll", &domain, &domain_len, - &msgid1, &msgid1_len, &msgid2, &msgid2_len, &count, &category) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(5, 5) + Z_PARAM_STR(domain) + Z_PARAM_STR(msgid1) + Z_PARAM_STR(msgid2) + Z_PARAM_LONG(count) + Z_PARAM_LONG(category) + ZEND_PARSE_PARAMETERS_END(); - PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len) - PHP_GETTEXT_LENGTH_CHECK(2, msgid1_len) - PHP_GETTEXT_LENGTH_CHECK(3, msgid2_len) + PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain)) + PHP_GETTEXT_LENGTH_CHECK(2, ZSTR_LEN(msgid1)) + PHP_GETTEXT_LENGTH_CHECK(3, ZSTR_LEN(msgid2)) PHP_DCGETTEXT_CATEGORY_CHECK(5, category) - msgstr = dcngettext(domain, msgid1, msgid2, count, category); + msgstr = dcngettext(ZSTR_VAL(domain), ZSTR_VAL(msgid1), ZSTR_VAL(msgid2), count, category); ZEND_ASSERT(msgstr); RETURN_STRING(msgstr); @@ -284,16 +301,23 @@ PHP_FUNCTION(dcngettext) /* {{{ Specify the character encoding in which the messages from the DOMAIN message catalog will be returned. */ PHP_FUNCTION(bind_textdomain_codeset) { - char *domain, *codeset = NULL, *retval = NULL; - size_t domain_len, codeset_len; + char *retval = NULL; + zend_string *domain, *codeset = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(domain) + Z_PARAM_OPTIONAL + Z_PARAM_STR_OR_NULL(codeset) + ZEND_PARSE_PARAMETERS_END(); + + PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, ZSTR_LEN(domain)) - if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss!", &domain, &domain_len, &codeset, &codeset_len) == FAILURE) { + if (!ZSTR_LEN(domain)) { + zend_argument_value_error(1, "cannot be empty"); RETURN_THROWS(); } - PHP_GETTEXT_DOMAIN_LENGTH_CHECK(1, domain_len) - - retval = bind_textdomain_codeset(domain, codeset); + retval = bind_textdomain_codeset(ZSTR_VAL(domain), codeset ? ZSTR_VAL(codeset) : NULL); if (!retval) { RETURN_FALSE; diff --git a/ext/gettext/gettext.stub.php b/ext/gettext/gettext.stub.php index ec6b197c0d77a..3e9a50177a592 100644 --- a/ext/gettext/gettext.stub.php +++ b/ext/gettext/gettext.stub.php @@ -3,7 +3,7 @@ /** @generate-class-entries */ /** @refcount 1 */ -function textdomain(?string $domain): string {} +function textdomain(?string $domain = null): string {} /** @refcount 1 */ function gettext(string $message): string {} @@ -18,7 +18,7 @@ function dgettext(string $domain, string $message): string {} function dcgettext(string $domain, string $message, int $category): string {} /** @refcount 1 */ -function bindtextdomain(string $domain, ?string $directory): string|false {} +function bindtextdomain(string $domain, ?string $directory = null): string|false {} #ifdef HAVE_NGETTEXT /** @refcount 1 */ @@ -37,5 +37,5 @@ function dcngettext(string $domain, string $singular, string $plural, int $count #ifdef HAVE_BIND_TEXTDOMAIN_CODESET /** @refcount 1 */ -function bind_textdomain_codeset(string $domain, ?string $codeset): string|false {} +function bind_textdomain_codeset(string $domain, ?string $codeset = null): string|false {} #endif diff --git a/ext/gettext/gettext_arginfo.h b/ext/gettext/gettext_arginfo.h index 8b2d7712dc87e..265f6cd900edc 100644 --- a/ext/gettext/gettext_arginfo.h +++ b/ext/gettext/gettext_arginfo.h @@ -1,8 +1,8 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 864b3389d4f99b0d7302ae399544e6fb9fb80b7e */ + * Stub hash: c675dc9492943bbac106c5906b75c31436964423 */ -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_textdomain, 0, 1, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, domain, IS_STRING, 1) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_textdomain, 0, 0, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, domain, IS_STRING, 1, "null") ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_gettext, 0, 1, IS_STRING, 0) @@ -22,9 +22,9 @@ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_dcgettext, 0, 3, IS_STRING, 0) ZEND_ARG_TYPE_INFO(0, category, IS_LONG, 0) ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_bindtextdomain, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_bindtextdomain, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, domain, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, directory, IS_STRING, 1) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, directory, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #if defined(HAVE_NGETTEXT) @@ -55,9 +55,9 @@ ZEND_END_ARG_INFO() #endif #if defined(HAVE_BIND_TEXTDOMAIN_CODESET) -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_bind_textdomain_codeset, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_bind_textdomain_codeset, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, domain, IS_STRING, 0) - ZEND_ARG_TYPE_INFO(0, codeset, IS_STRING, 1) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, codeset, IS_STRING, 1, "null") ZEND_END_ARG_INFO() #endif diff --git a/ext/gettext/tests/gettext_textdomain-retval.phpt b/ext/gettext/tests/gettext_textdomain-retval.phpt index 172a0069afc7f..96b29c7bf946c 100644 --- a/ext/gettext/tests/gettext_textdomain-retval.phpt +++ b/ext/gettext/tests/gettext_textdomain-retval.phpt @@ -19,6 +19,12 @@ echo textdomain('test'), "\n"; echo textdomain(null), "\n"; echo textdomain('foo'), "\n"; +try { + textdomain('0'); +} catch (\ValueError $e) { + echo $e->getMessage() . PHP_EOL; +} + try { textdomain(''); } catch (\ValueError $e) { @@ -29,6 +35,7 @@ try { test test foo +textdomain(): Argument #1 ($domain) cannot be zero textdomain(): Argument #1 ($domain) cannot be empty --CREDITS-- Christian Weiske, cweiske@php.net From 1cf4cc3894bfcecfc36b5681204d101992d76e5f Mon Sep 17 00:00:00 2001 From: David Carlier Date: Thu, 21 Mar 2024 19:44:02 +0000 Subject: [PATCH 015/201] ext/intl: IntlDateFormatter::parseToCalendar addition. Unlike IntlDateFormatter::parse, the timezone is updated accordingly. Close GH-13779 --- NEWS | 1 + UPGRADING | 2 + ext/intl/dateformat/dateformat.stub.php | 5 ++ ext/intl/dateformat/dateformat_arginfo.h | 9 ++- ext/intl/dateformat/dateformat_parse.c | 70 +++++++++++++++++++++--- ext/intl/tests/gh13766.phpt | 35 ++++++++++++ 6 files changed, 113 insertions(+), 9 deletions(-) create mode 100644 ext/intl/tests/gh13766.phpt diff --git a/NEWS b/NEWS index 86af67c92d7ef..1d341a9c3e57e 100644 --- a/NEWS +++ b/NEWS @@ -86,6 +86,7 @@ PHP NEWS . ResourceBundle::get() now has a tentative return type of: ResourceBundle|array|string|int|null . Added the new Grapheme function grapheme_str_split. (youkidearitai) + . Addewd IntlDateFormatter::parseToCalendar. (David Carlier) - LDAP: . Added LDAP_OPT_X_TLS_PROTOCOL_MAX/LDAP_OPT_X_TLS_PROTOCOL_TLS1_3 diff --git a/UPGRADING b/UPGRADING index 54539145de83a..0cb4bf305b4ee 100644 --- a/UPGRADING +++ b/UPGRADING @@ -469,6 +469,8 @@ PHP 8.4 UPGRADE NOTES the IANA identifier from a given timezone. . Added grapheme_str_split which allow to support emoji and Variation Selectors. + . Added IntlDateFormatter::parseToCalendar which behaves like + IntlDateFormatter::parse except the time zone is updated. - MBString: . Added mb_trim, mb_ltrim and mb_rtrim functions. diff --git a/ext/intl/dateformat/dateformat.stub.php b/ext/intl/dateformat/dateformat.stub.php index 06f26b0658739..7582af2717fbe 100644 --- a/ext/intl/dateformat/dateformat.stub.php +++ b/ext/intl/dateformat/dateformat.stub.php @@ -159,6 +159,11 @@ public static function formatObject($datetime, $format = null, ?string $locale = */ public function parse(string $string, &$offset = null): int|float|false {} + /** + * @param int $offset + */ + public function parseToCalendar(string $string, &$offset = null): int|float|false {} + /** * @param int $offset * @return array|false diff --git a/ext/intl/dateformat/dateformat_arginfo.h b/ext/intl/dateformat/dateformat_arginfo.h index 4d16a26ef1673..df1de7d6895f4 100644 --- a/ext/intl/dateformat/dateformat_arginfo.h +++ b/ext/intl/dateformat/dateformat_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 91f1dbe4843fd1d4dff7266e814a3c8f9aed882a */ + * Stub hash: 56b66b1b51220ddbff698ec4c9a6ae60f3e0bfb0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_IntlDateFormatter___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, locale, IS_STRING, 1) @@ -75,6 +75,11 @@ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_IntlDateFormatte ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, offset, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_class_IntlDateFormatter_parseToCalendar, 0, 1, MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_FALSE) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) + ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, offset, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_MASK_EX(arginfo_class_IntlDateFormatter_localtime, 0, 1, MAY_BE_ARRAY|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) ZEND_ARG_INFO_WITH_DEFAULT_VALUE(1, offset, "null") @@ -104,6 +109,7 @@ ZEND_FUNCTION(datefmt_is_lenient); ZEND_FUNCTION(datefmt_format); ZEND_FUNCTION(datefmt_format_object); ZEND_FUNCTION(datefmt_parse); +ZEND_METHOD(IntlDateFormatter, parseToCalendar); ZEND_FUNCTION(datefmt_localtime); ZEND_FUNCTION(datefmt_get_error_code); ZEND_FUNCTION(datefmt_get_error_message); @@ -127,6 +133,7 @@ static const zend_function_entry class_IntlDateFormatter_methods[] = { ZEND_RAW_FENTRY("format", zif_datefmt_format, arginfo_class_IntlDateFormatter_format, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("formatObject", zif_datefmt_format_object, arginfo_class_IntlDateFormatter_formatObject, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC, NULL, NULL) ZEND_RAW_FENTRY("parse", zif_datefmt_parse, arginfo_class_IntlDateFormatter_parse, ZEND_ACC_PUBLIC, NULL, NULL) + ZEND_ME(IntlDateFormatter, parseToCalendar, arginfo_class_IntlDateFormatter_parseToCalendar, ZEND_ACC_PUBLIC) ZEND_RAW_FENTRY("localtime", zif_datefmt_localtime, arginfo_class_IntlDateFormatter_localtime, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getErrorCode", zif_datefmt_get_error_code, arginfo_class_IntlDateFormatter_getErrorCode, ZEND_ACC_PUBLIC, NULL, NULL) ZEND_RAW_FENTRY("getErrorMessage", zif_datefmt_get_error_message, arginfo_class_IntlDateFormatter_getErrorMessage, ZEND_ACC_PUBLIC, NULL, NULL) diff --git a/ext/intl/dateformat/dateformat_parse.c b/ext/intl/dateformat/dateformat_parse.c index 69aeff886d3b9..82b5a71bbee8f 100644 --- a/ext/intl/dateformat/dateformat_parse.c +++ b/ext/intl/dateformat/dateformat_parse.c @@ -31,7 +31,7 @@ * if set to 1 - store any error encountered in the parameter parse_error * if set to 0 - no need to store any error encountered in the parameter parse_error */ -static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, zval *return_value) +static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* text_to_parse, size_t text_len, int32_t *parse_pos, bool update_calendar, zval *return_value) { double result = 0; UDate timestamp =0; @@ -42,13 +42,22 @@ static void internal_parse_to_timestamp(IntlDateFormatter_object *dfo, char* tex intl_convert_utf8_to_utf16(&text_utf16, &text_utf16_len, text_to_parse, text_len, &INTL_DATA_ERROR_CODE(dfo)); INTL_METHOD_CHECK_STATUS(dfo, "Error converting timezone to UTF-16" ); - timestamp = udat_parse( DATE_FORMAT_OBJECT(dfo), text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo)); - if( text_utf16 ){ - efree(text_utf16); + if (UNEXPECTED(update_calendar)) { + UCalendar *parsed_calendar = (UCalendar *)udat_getCalendar(DATE_FORMAT_OBJECT(dfo)); + udat_parseCalendar(DATE_FORMAT_OBJECT(dfo), parsed_calendar, text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo)); + if (text_utf16) { + efree(text_utf16); + } + INTL_METHOD_CHECK_STATUS( dfo, "Calendar parsing failed" ); + timestamp = ucal_getMillis( parsed_calendar, &INTL_DATA_ERROR_CODE(dfo)); + } else { + timestamp = udat_parse(DATE_FORMAT_OBJECT(dfo), text_utf16, text_utf16_len, parse_pos, &INTL_DATA_ERROR_CODE(dfo)); + if (text_utf16) { + efree(text_utf16); + } } INTL_METHOD_CHECK_STATUS( dfo, "Date parsing failed" ); - /* Since return is in sec. */ result = (double)timestamp / U_MILLIS_PER_SECOND; if (result > (double)LONG_MAX || result < (double)LONG_MIN) { @@ -145,18 +154,63 @@ PHP_FUNCTION(datefmt_parse) RETURN_FALSE; } parse_pos = (int32_t)long_parse_pos; - if((size_t)parse_pos > text_len) { + if ((size_t)parse_pos > text_len) { RETURN_FALSE; } } - internal_parse_to_timestamp( dfo, text_to_parse, text_len, z_parse_pos?&parse_pos:NULL, return_value); - if(z_parse_pos) { + internal_parse_to_timestamp( dfo, text_to_parse, text_len, z_parse_pos ? &parse_pos : NULL, false, return_value); + if (z_parse_pos) { zval_ptr_dtor(z_parse_pos); ZVAL_LONG(z_parse_pos, parse_pos); } } /* }}} */ +PHP_METHOD(IntlDateFormatter, parseToCalendar) +{ + zend_string *text_to_parse = NULL; + zval* z_parse_pos = NULL; + int32_t parse_pos = -1; + + DATE_FORMAT_METHOD_INIT_VARS; + + ZEND_PARSE_PARAMETERS_START(1, 2) + Z_PARAM_STR(text_to_parse) + Z_PARAM_OPTIONAL + Z_PARAM_ZVAL(z_parse_pos) + ZEND_PARSE_PARAMETERS_END(); + + object = ZEND_THIS; + + /* Fetch the object. */ + DATE_FORMAT_METHOD_FETCH_OBJECT; + + if (z_parse_pos) { + zend_long long_parse_pos; + ZVAL_DEREF(z_parse_pos); + bool failed = false; + long_parse_pos = zval_try_get_long(z_parse_pos, &failed); + if (failed) { + zend_argument_type_error(2, "must be of type int, %s given", zend_zval_value_name(z_parse_pos)); + RETURN_THROWS(); + } + if (ZEND_LONG_INT_OVFL(long_parse_pos)) { + intl_error_set_code(NULL, U_ILLEGAL_ARGUMENT_ERROR); + intl_error_set_custom_msg(NULL, "String index is out of valid range.", 0); + RETURN_FALSE; + } + parse_pos = (int32_t)long_parse_pos; + if (parse_pos != -1 && (size_t)parse_pos > ZSTR_LEN(text_to_parse)) { + RETURN_FALSE; + } + } + internal_parse_to_timestamp( dfo, ZSTR_VAL(text_to_parse), ZSTR_LEN(text_to_parse), z_parse_pos ? &parse_pos : NULL, true, return_value); + if (z_parse_pos) { + zval_ptr_dtor(z_parse_pos); + ZVAL_LONG(z_parse_pos, parse_pos); + } +} + /* {{{ Parse the string $value to a localtime array */ PHP_FUNCTION(datefmt_localtime) { diff --git a/ext/intl/tests/gh13766.phpt b/ext/intl/tests/gh13766.phpt new file mode 100644 index 0000000000000..b01b557e497a6 --- /dev/null +++ b/ext/intl/tests/gh13766.phpt @@ -0,0 +1,35 @@ +--TEST-- +IntlDateFormatter::parse update its calendar +--EXTENSIONS-- +intl +--FILE-- +setTimeZone('Europe/Berlin'); +$oIntlDateFormatter->setPattern('VV'); + +var_dump($oIntlDateFormatter->parse('America/Los_Angeles', $offset1)); +var_dump($oIntlDateFormatter->getTimeZone()->getID()); +var_dump($oIntlDateFormatter->parseToCalendar('America/Los_Angeles', $offset2)); +var_dump($oIntlDateFormatter->getTimeZone()->getID()); +$offset3 = "offset"; + +try { + $oIntlDateFormatter->parseToCalendar('America/Los_Angeles', $offset3); +} catch (\TypeError $e) { + echo $e->getMessage() . PHP_EOL; +} +$offset3 = PHP_INT_MAX * 16; +try { + $oIntlDateFormatter->parseToCalendar('America/Los_Angeles', $offset3); +} catch (\ValueError $e) { + echo $e->getMessage(); +} +--EXPECTF-- +int(%d) +string(13) "Europe/Berlin" +int(%d) +string(19) "America/Los_Angeles" +IntlDateFormatter::parseToCalendar(): Argument #2 ($offset) must be of type int, string given + +Deprecated: Implicit conversion from float 1.4757395258967641E+20 to int loses precision in %s on line %d From 7f3fd30c3be520393074aa8559bd97dfce6a6463 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 30 Apr 2024 11:40:32 +0200 Subject: [PATCH 016/201] [skip ci] Fix intl test on 32-bit machines --- ext/intl/tests/gh13766.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/intl/tests/gh13766.phpt b/ext/intl/tests/gh13766.phpt index b01b557e497a6..70567fa860537 100644 --- a/ext/intl/tests/gh13766.phpt +++ b/ext/intl/tests/gh13766.phpt @@ -32,4 +32,4 @@ int(%d) string(19) "America/Los_Angeles" IntlDateFormatter::parseToCalendar(): Argument #2 ($offset) must be of type int, string given -Deprecated: Implicit conversion from float 1.4757395258967641E+20 to int loses precision in %s on line %d +Deprecated: Implicit conversion from float %r(1\.4757395258967641E\+20|34359738352)%r to int loses precision in %s on line %d From cb55588a7ec11f528ccc7548beafabdbc7975889 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sun, 28 Apr 2024 21:47:13 +0200 Subject: [PATCH 017/201] Skip online tests by default Fixes GH-14058 Closes GH-14070 --- UPGRADING | 4 ++++ run-tests.php | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/UPGRADING b/UPGRADING index 0cb4bf305b4ee..9456f99fede08 100644 --- a/UPGRADING +++ b/UPGRADING @@ -652,6 +652,10 @@ PHP 8.4 UPGRADE NOTES and the line of definition to make them easier to distinguish, for example within stack traces. +* run-tests.php now skips online tests by default. Set the SKIP_ONLINE_TESTS + environment variable to 0, or pass the --online flag to run-tests.php to + execute them. + ======================================== 14. Performance Improvements ======================================== diff --git a/run-tests.php b/run-tests.php index 5837a1a85000c..5314a26f48550 100755 --- a/run-tests.php +++ b/run-tests.php @@ -75,9 +75,11 @@ function show_usage(): void -s Write output to . - -x Sets 'SKIP_SLOW_TESTS' environmental variable. + -x Sets 'SKIP_SLOW_TESTS' environment variable. - --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. + --online Prevents setting the 'SKIP_ONLINE_TESTS' environment variable. + + --offline Sets 'SKIP_ONLINE_TESTS' environment variable (default). --verbose -v Verbose mode. @@ -355,6 +357,7 @@ function main(): void $num_repeats = 1; $show_progress = true; $ignored_by_ext = []; + $online = null; $cfgtypes = ['show', 'keep']; $cfgfiles = ['skip', 'php', 'clean', 'out', 'diff', 'exp', 'mem']; @@ -553,8 +556,11 @@ function main(): void case 'x': $environment['SKIP_SLOW_TESTS'] = 1; break; + case '--online': + $online = true; + break; case '--offline': - $environment['SKIP_ONLINE_TESTS'] = 1; + $online = false; break; case '--shuffle': $shuffle = true; @@ -637,6 +643,13 @@ function main(): void } } + if ($online === null && !isset($environment['SKIP_ONLINE_TESTS'])) { + $online = false; + } + if ($online !== null) { + $environment['SKIP_ONLINE_TESTS'] = $online ? '0' : '1'; + } + if ($selected_tests && count($test_files) === 0) { echo "No tests found.\n"; return; From d52605db15f3e1d0dcbca778e495edd048f9ae7f Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Sun, 28 Apr 2024 21:52:43 +0200 Subject: [PATCH 018/201] Replace xhtml image with base64 data --- sapi/fpm/status.html.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sapi/fpm/status.html.in b/sapi/fpm/status.html.in index b87a446f59f66..b14123dd323ac 100644 --- a/sapi/fpm/status.html.in +++ b/sapi/fpm/status.html.in @@ -72,7 +72,7 @@

- Valid XHTML 1.0 Transitional + Valid XHTML 1.0 Transitional