From e034b69fa6f9f3ac9c409f108ae91b4f682660d7 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sun, 30 Mar 2025 20:07:49 +0200 Subject: [PATCH 1/5] Optimize SplFixedArray::fromArray() for packed arrays (#18196) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the array is packed, then we don't have to loop to get the highest index. For this script: ```php $array = range(1, 100); for ($i=0;$i<1000000;$i++) { SplFixedArray::fromArray($array); } ``` On an i7-4790: ``` Benchmark 1: ./sapi/cli/php spl.php Time (mean ± σ): 376.5 ms ± 2.0 ms [User: 372.1 ms, System: 2.6 ms] Range (min … max): 373.7 ms … 379.5 ms 10 runs Benchmark 2: ./sapi/cli/php_old spl.php Time (mean ± σ): 511.6 ms ± 1.9 ms [User: 508.0 ms, System: 2.3 ms] Range (min … max): 509.2 ms … 515.1 ms 10 runs Summary ./sapi/cli/php spl.php ran 1.36 ± 0.01 times faster than ./sapi/cli/php_old spl.php ``` On an i7-1185G7: ``` Benchmark 1: ./sapi/cli/php spl.php Time (mean ± σ): 250.4 ms ± 3.5 ms [User: 246.6 ms, System: 2.6 ms] Range (min … max): 247.0 ms … 258.5 ms 11 runs Benchmark 2: ./sapi/cli/php_old spl.php Time (mean ± σ): 328.4 ms ± 1.0 ms [User: 324.4 ms, System: 3.8 ms] Range (min … max): 327.5 ms … 331.0 ms 10 runs Summary ./sapi/cli/php spl.php ran 1.31 ± 0.02 times faster than ./sapi/cli/php_old spl.php ``` Bonus: this also decreases the code size of the function. --- ext/spl/spl_fixedarray.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/ext/spl/spl_fixedarray.c b/ext/spl/spl_fixedarray.c index b187949699087..99e6983b0ffc0 100644 --- a/ext/spl/spl_fixedarray.c +++ b/ext/spl/spl_fixedarray.c @@ -733,22 +733,29 @@ PHP_METHOD(SplFixedArray, fromArray) zend_ulong num_index, max_index = 0; zend_long tmp; - ZEND_HASH_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) { - if (str_index != NULL || (zend_long)num_index < 0) { - zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys"); + if (HT_IS_PACKED(Z_ARRVAL_P(data))) { + /* If there are no holes, then nNumUsed is the number of elements. + * If there are holes, then nNumUsed is the index of the last element. */ + tmp = Z_ARRVAL_P(data)->nNumUsed; + } else { + ZEND_HASH_MAP_FOREACH_KEY(Z_ARRVAL_P(data), num_index, str_index) { + if (str_index != NULL || (zend_long)num_index < 0) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "array must contain only positive integer keys"); + RETURN_THROWS(); + } + + if (num_index > max_index) { + max_index = num_index; + } + } ZEND_HASH_FOREACH_END(); + + tmp = max_index + 1; + if (tmp <= 0) { + zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected"); RETURN_THROWS(); } - - if (num_index > max_index) { - max_index = num_index; - } - } ZEND_HASH_FOREACH_END(); - - tmp = max_index + 1; - if (tmp <= 0) { - zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0, "integer overflow detected"); - RETURN_THROWS(); } + spl_fixedarray_init(&array, tmp); ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(data), num_index, element) { From 5544a77f770f081224cb0fa426fc8020e6220223 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 3 Mar 2025 20:22:35 +0000 Subject: [PATCH 2/5] Move definition of php_le_stream_context From ext/standard/file.h to main/streams/php_stream_context.h This reduces some dependency of main/ on ext/standard --- ext/standard/file.h | 1 - main/streams/php_stream_context.h | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/standard/file.h b/ext/standard/file.h index 3a9cf1435b143..f8faebd028293 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -36,7 +36,6 @@ PHPAPI PHP_FUNCTION(fpassthru); PHP_MINIT_FUNCTION(user_streams); -PHPAPI int php_le_stream_context(void); PHPAPI zend_result php_copy_file(const char *src, const char *dest); PHPAPI zend_result php_copy_file_ex(const char *src, const char *dest, int src_flags); PHPAPI zend_result php_copy_file_ctx(const char *src, const char *dest, int src_flags, php_stream_context *ctx); diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index d4ebe29bc162e..677bf14befeb2 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -53,6 +53,7 @@ struct _php_stream_context { }; BEGIN_EXTERN_C() +PHPAPI int php_le_stream_context(void); PHPAPI void php_stream_context_free(php_stream_context *context); PHPAPI php_stream_context *php_stream_context_alloc(void); PHPAPI zval *php_stream_context_get_option(php_stream_context *context, From 908490764b6b708ff06b1945f34568f44170ef16 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 3 Mar 2025 20:23:27 +0000 Subject: [PATCH 3/5] main/streams: Add a helper macro to retrieve default context --- main/streams/php_stream_context.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/main/streams/php_stream_context.h b/main/streams/php_stream_context.h index 677bf14befeb2..a5325a94642c9 100644 --- a/main/streams/php_stream_context.h +++ b/main/streams/php_stream_context.h @@ -25,14 +25,17 @@ typedef void (*php_stream_notification_func)(php_stream_context *context, #define PHP_STREAM_NOTIFIER_PROGRESS 1 +/* TODO: Remove dependence on ext/standard/file.h for the default context global */ +#define php_stream_context_get_default(without_context) \ + (without_context) ? NULL : FG(default_context) ? FG(default_context) : \ + (FG(default_context) = php_stream_context_alloc()) + /* Attempt to fetch context from the zval passed, If no context was passed, use the default context The default context has not yet been created, do it now. */ #define php_stream_context_from_zval(zcontext, nocontext) ( \ (zcontext) ? zend_fetch_resource_ex(zcontext, "Stream-Context", php_le_stream_context()) : \ - (nocontext) ? NULL : \ - FG(default_context) ? FG(default_context) : \ - (FG(default_context) = php_stream_context_alloc()) ) + php_stream_context_get_default(nocontext)) #define php_stream_context_to_zval(context, zval) { ZVAL_RES(zval, (context)->res); GC_ADDREF((context)->res); } From 2244810dcf5d0deaa14cf101ab32f70e1e1fe941 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Mon, 17 Mar 2025 14:29:41 +0000 Subject: [PATCH 4/5] main/streams: Add a new helper function to get a php_stream from a zval without errors This is intended to replace the few manual usages of zend_fetch_resource2_ex() to fetch a php_stream from a zval. This will simplify the conversion from resource to object for streams when this actually happens. --- main/php_streams.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/main/php_streams.h b/main/php_streams.h index f534bc27285f1..02281c3913fcd 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -285,6 +285,10 @@ END_EXTERN_C() #define php_stream_from_res_no_verify(xstr, pzval) (xstr) = (php_stream*)zend_fetch_resource2((res), "stream", php_file_le_stream(), php_file_le_pstream()) #define php_stream_from_zval_no_verify(xstr, pzval) (xstr) = (php_stream*)zend_fetch_resource2_ex((pzval), "stream", php_file_le_stream(), php_file_le_pstream()) +static zend_always_inline php_stream* php_stream_from_zval_no_verify_no_error(zval *zval) { + return (php_stream*)zend_fetch_resource2_ex(zval, NULL, php_file_le_stream(), php_file_le_pstream()); +} + BEGIN_EXTERN_C() static zend_always_inline bool php_stream_zend_parse_arg_into_stream( From 334d9bbc09e37d6f66dde23988df7d7299dc6f19 Mon Sep 17 00:00:00 2001 From: David Carlier Date: Sun, 30 Mar 2025 19:34:52 +0100 Subject: [PATCH 5/5] ext/pgsql: adding pg_service() alongside other connection infos. returns the ongoing name of the service, if there is. available since postgres 18 close GH-18198 --- NEWS | 2 ++ UPGRADING | 1 + ext/pgsql/config.m4 | 3 +++ ext/pgsql/pgsql.c | 14 ++++++++++++++ ext/pgsql/pgsql.stub.php | 3 +++ ext/pgsql/pgsql_arginfo.h | 14 +++++++++++++- ext/pgsql/tests/pg_service.phpt | 19 +++++++++++++++++++ 7 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 ext/pgsql/tests/pg_service.phpt diff --git a/NEWS b/NEWS index 23bb612c482ca..dc91f999fb15e 100644 --- a/NEWS +++ b/NEWS @@ -110,6 +110,8 @@ PHP NEWS . pg_connect checks if connection_string contains any null byte, pg_close_stmt check if the statement contains any null byte. (David Carlier) + . Added pg_service to get the connection current service identifier. + (David Carlier) - POSIX: . Added POSIX_SC_OPEN_MAX constant to get the number of file descriptors diff --git a/UPGRADING b/UPGRADING index 7488912c88804..1a525f22f4411 100644 --- a/UPGRADING +++ b/UPGRADING @@ -273,6 +273,7 @@ PHP 8.5 UPGRADE NOTES . pg_close_stmt offers an alternative way to close a prepared statement from the DEALLOCATE sql command in that we can reuse its name afterwards. + . pg_service returns the ongoing service name of the connection. - Reflection: . ReflectionConstant::getFileName() was introduced. diff --git a/ext/pgsql/config.m4 b/ext/pgsql/config.m4 index 48fbbae34ace0..1409f879b52cc 100644 --- a/ext/pgsql/config.m4 +++ b/ext/pgsql/config.m4 @@ -31,6 +31,9 @@ if test "$PHP_PGSQL" != "no"; then PHP_CHECK_LIBRARY([pq], [PQclosePrepared], [AC_DEFINE([HAVE_PG_CLOSE_STMT], [1], [PostgreSQL 17 or later])],, [$PGSQL_LIBS]) + PHP_CHECK_LIBRARY([pq], [PQservice], + [AC_DEFINE([HAVE_PG_SERVICE], [1], [PostgreSQL 18 or later])],, + [$PGSQL_LIBS]) old_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $PGSQL_CFLAGS" diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index 3f16c2e88f2fa..c53bf8ed9a35c 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -907,6 +907,7 @@ PHP_FUNCTION(pg_close) #define PHP_PG_HOST 6 #define PHP_PG_VERSION 7 #define PHP_PG_JIT 8 +#define PHP_PG_SERVICE 9 /* php_pgsql_get_link_info */ static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type) @@ -991,6 +992,12 @@ static void php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAMETERS, int entry_type PQclear(res); return; } +#if defined(HAVE_PG_SERVICE) + case PHP_PG_SERVICE: { + result = PQservice(pgsql); + break; + } +#endif EMPTY_SWITCH_DEFAULT_CASE() } if (result) { @@ -1047,6 +1054,13 @@ PHP_FUNCTION(pg_jit) php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_JIT); } +#if defined(HAVE_PG_SERVICE) +PHP_FUNCTION(pg_service) +{ + php_pgsql_get_link_info(INTERNAL_FUNCTION_PARAM_PASSTHRU,PHP_PG_SERVICE); +} +#endif + /* Returns the value of a server parameter */ PHP_FUNCTION(pg_parameter_status) { diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index cac22e3a30301..04e648eff8d50 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -508,6 +508,9 @@ function pg_version(?PgSql\Connection $connection = null): array {} */ function pg_jit(?PgSql\Connection $connection = null): array {} +#ifdef HAVE_PG_SERVICE + function pg_service(?PgSql\Connection $connection = null): string {} +#endif /** * @param PgSql\Connection|string $connection * @refcount 1 diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 336dd7ff86d04..cb58645e5e9f5 100644 --- a/ext/pgsql/pgsql_arginfo.h +++ b/ext/pgsql/pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 49e3493be11a5da1ed9a57339f14f92f34bf5d1b */ + * Stub hash: 3cf44ca06d11cad086829d3d04900ade3cacb88b */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_pg_connect, 0, 1, PgSql\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, connection_string, IS_STRING, 0) @@ -38,6 +38,12 @@ ZEND_END_ARG_INFO() #define arginfo_pg_jit arginfo_pg_version +#if defined(HAVE_PG_SERVICE) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_pg_service, 0, 0, IS_STRING, 0) + ZEND_ARG_OBJ_INFO_WITH_DEFAULT_VALUE(0, connection, PgSql\\Connection, 1, "null") +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_pg_parameter_status, 0, 1, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_INFO(0, connection) ZEND_ARG_TYPE_INFO(0, name, IS_STRING, 0) @@ -514,6 +520,9 @@ ZEND_FUNCTION(pg_tty); ZEND_FUNCTION(pg_host); ZEND_FUNCTION(pg_version); ZEND_FUNCTION(pg_jit); +#if defined(HAVE_PG_SERVICE) +ZEND_FUNCTION(pg_service); +#endif ZEND_FUNCTION(pg_parameter_status); ZEND_FUNCTION(pg_ping); ZEND_FUNCTION(pg_query); @@ -623,6 +632,9 @@ static const zend_function_entry ext_functions[] = { ZEND_FE(pg_host, arginfo_pg_host) ZEND_FE(pg_version, arginfo_pg_version) ZEND_FE(pg_jit, arginfo_pg_jit) +#if defined(HAVE_PG_SERVICE) + ZEND_FE(pg_service, arginfo_pg_service) +#endif ZEND_FE(pg_parameter_status, arginfo_pg_parameter_status) ZEND_FE(pg_ping, arginfo_pg_ping) ZEND_FE(pg_query, arginfo_pg_query) diff --git a/ext/pgsql/tests/pg_service.phpt b/ext/pgsql/tests/pg_service.phpt new file mode 100644 index 0000000000000..0ce1be7285ebc --- /dev/null +++ b/ext/pgsql/tests/pg_service.phpt @@ -0,0 +1,19 @@ +--TEST-- +PostgreSQL connection service field support +--EXTENSIONS-- +pgsql +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +string(%d) "%A"