diff --git a/NEWS b/NEWS index 42168212aa523..d7aaf5df0d908 100644 --- a/NEWS +++ b/NEWS @@ -245,6 +245,8 @@ PHP NEWS . Fixed bug #49278 (SoapClient::__getLastResponseHeaders returns NULL if wsdl operation !has output). (nielsdos) . Fixed bug #44383 (PHP DateTime not converted to xsd:datetime). (nielsdos) + . Fixed bug GH-11941 (soap with session persistence will silently fail when + "session" built as a shared object). (nielsdos) - Sockets: . Removed the deprecated inet_ntoa call support. (David Carlier) diff --git a/UPGRADING b/UPGRADING index ab17035d54adf..d9d3f03b9ec1a 100644 --- a/UPGRADING +++ b/UPGRADING @@ -145,6 +145,10 @@ PHP 8.4 UPGRADE NOTES . SoapClient::$typemap is now an array rather than a resource. Checks using is_resource() (i.e. is_resource($client->typemap)) should be replaced with checks for null (i.e. $client->typemap !== null). + . The SOAP extension gained an optional dependency on the session extension. + If you build PHP without the session extension and with --enable-rtld-now, + you will experience errors on startup if you also use the SOAP extension. + To solve this, either don't use rtld-now or load the session extension. - SPL: . Out of bounds accesses in SplFixedArray now throw an exception of type @@ -300,6 +304,7 @@ PHP 8.4 UPGRADE NOTES . Instances of DateTimeInterface that are passed to xsd:datetime or similar elements are now serialized as such instead of being serialized as an empty string. + . Session persistence now works with a shared session module. - XSL: . It is now possible to use parameters that contain both single and double diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index f3fece4ed00bb..8f43e83933f6f 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -253,6 +253,12 @@ PHP 8.4 INTERNALS UPGRADE NOTES Moreover, providing it with a binary safe string is the responsibility of the caller now. + h. ext/session + - Added the php_get_session_status() API to get the session status, which is + equivalent to reading PS(session_status) but works with shared objects too. + - Added the php_get_session_var_str() API to set a session variable without + needing to create a zend_string. + ======================== 4. OpCode changes ======================== diff --git a/ext/session/php_session.h b/ext/session/php_session.h index c9611e6159709..3b728090898c6 100644 --- a/ext/session/php_session.h +++ b/ext/session/php_session.h @@ -256,6 +256,7 @@ PHPAPI zend_result php_session_destroy(void); PHPAPI void php_add_session_var(zend_string *name); PHPAPI zval *php_set_session_var(zend_string *name, zval *state_val, php_unserialize_data_t *var_hash); PHPAPI zval *php_get_session_var(zend_string *name); +PHPAPI zval* php_get_session_var_str(const char *name, size_t name_len); PHPAPI zend_result php_session_register_module(const ps_module *); @@ -265,6 +266,7 @@ PHPAPI zend_result php_session_register_serializer(const char *name, PHPAPI zend_result php_session_start(void); PHPAPI zend_result php_session_flush(int write); +PHPAPI php_session_status php_get_session_status(void); PHPAPI const ps_module *_php_find_ps_module(const char *name); PHPAPI const ps_serializer *_php_find_ps_serializer(const char *name); diff --git a/ext/session/session.c b/ext/session/session.c index f3296e37f271a..4a369898a8eb2 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -225,6 +225,14 @@ PHPAPI zval* php_get_session_var(zend_string *name) /* {{{ */ } /* }}} */ +PHPAPI zval* php_get_session_var_str(const char *name, size_t name_len) +{ + IF_SESSION_VARS() { + return zend_hash_str_find(Z_ARRVAL_P(Z_REFVAL(PS(http_session_vars))), name, name_len); + } + return NULL; +} + static void php_session_track_init(void) /* {{{ */ { zval session_vars; @@ -1632,6 +1640,11 @@ PHPAPI zend_result php_session_flush(int write) /* {{{ */ } /* }}} */ +PHPAPI php_session_status php_get_session_status(void) +{ + return PS(session_status); +} + static zend_result php_session_abort(void) /* {{{ */ { if (PS(session_status) == php_session_active) { diff --git a/ext/soap/config.m4 b/ext/soap/config.m4 index 879035f33417b..a83424ef39702 100644 --- a/ext/soap/config.m4 +++ b/ext/soap/config.m4 @@ -10,4 +10,5 @@ if test "$PHP_SOAP" != "no"; then PHP_SUBST(SOAP_SHARED_LIBADD) ]) PHP_ADD_EXTENSION_DEP(soap, libxml) + PHP_ADD_EXTENSION_DEP(soap, session, true) fi diff --git a/ext/soap/config.w32 b/ext/soap/config.w32 index fa5c76588d004..31e1f0da39ce0 100644 --- a/ext/soap/config.w32 +++ b/ext/soap/config.w32 @@ -10,6 +10,7 @@ if (PHP_SOAP != "no") { ) { EXTENSION('soap', 'soap.c php_encoding.c php_http.c php_packet_soap.c php_schema.c php_sdl.c php_xml.c', null, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1"); AC_DEFINE('HAVE_SOAP', 1, "SOAP support"); + ADD_EXTENSION_DEP('soap', 'session', true); if (!PHP_SOAP_SHARED) { ADD_FLAG('CFLAGS_SOAP', "/D LIBXML_STATIC "); diff --git a/ext/soap/soap.c b/ext/soap/soap.c index b42917f8ad12c..09b9f8f5fba17 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -20,14 +20,19 @@ #include "config.h" #endif #include "php_soap.h" -#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION) #include "ext/session/php_session.h" -#endif #include "soap_arginfo.h" #include "zend_exceptions.h" #include "zend_interfaces.h" #include "ext/standard/php_incomplete_class.h" +/* We only have session support if PHP was configured with session support + * or if the session module could be loaded dynamically, which will only + * work if soap is loaded dynamically as well. */ +#if defined(HAVE_PHP_SESSION) || defined(COMPILE_DL_SOAP) +# define SOAP_HAS_SESSION_SUPPORT +#endif + typedef struct _soapHeader { sdlFunctionPtr function; zval function_name; @@ -298,6 +303,7 @@ PHP_MINFO_FUNCTION(soap); static const zend_module_dep soap_deps[] = { ZEND_MOD_REQUIRED("date") ZEND_MOD_REQUIRED("libxml") + ZEND_MOD_OPTIONAL("session") ZEND_MOD_END }; @@ -995,6 +1001,12 @@ PHP_METHOD(SoapServer, setPersistence) if (service->type == SOAP_CLASS) { if (value == SOAP_PERSISTENCE_SESSION || value == SOAP_PERSISTENCE_REQUEST) { + if (value == SOAP_PERSISTENCE_SESSION && !zend_hash_str_exists(&module_registry, "session", sizeof("session")-1)) { + SOAP_SERVER_END_CODE(); + zend_throw_error(NULL, "SoapServer::setPersistence(): Session persistence cannot be enabled because the session module is not enabled"); + RETURN_THROWS(); + } + service->soap_class.persistence = value; } else { zend_argument_value_error( @@ -1406,22 +1418,18 @@ PHP_METHOD(SoapServer, handle) soap_obj = &service->soap_object; function_table = &((Z_OBJCE_P(soap_obj))->function_table); } else if (service->type == SOAP_CLASS) { -#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION) - /* If persistent then set soap_obj from from the previous created session (if available) */ + /* If persistent then set soap_obj from the previous created session (if available) */ +#ifdef SOAP_HAS_SESSION_SUPPORT if (service->soap_class.persistence == SOAP_PERSISTENCE_SESSION) { - zval *session_vars, *tmp_soap_p; - - if (PS(session_status) != php_session_active && - PS(session_status) != php_session_disabled) { + php_session_status session_status = php_get_session_status(); + if (session_status != php_session_active && + session_status != php_session_disabled) { php_session_start(); } /* Find the soap object and assign */ - session_vars = &PS(http_session_vars); - ZVAL_DEREF(session_vars); - if (Z_TYPE_P(session_vars) == IS_ARRAY && - (tmp_soap_p = zend_hash_str_find(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1)) != NULL && - Z_TYPE_P(tmp_soap_p) == IS_OBJECT) { + zval *tmp_soap_p = php_get_session_var_str("_bogus_session_name", sizeof("_bogus_session_name")-1); + if (tmp_soap_p != NULL && Z_TYPE_P(tmp_soap_p) == IS_OBJECT) { if (EXPECTED(Z_OBJCE_P(tmp_soap_p) == service->soap_class.ce)) { soap_obj = tmp_soap_p; } else if (Z_OBJCE_P(tmp_soap_p) == php_ce_incomplete_class) { @@ -1431,9 +1439,9 @@ PHP_METHOD(SoapServer, handle) } } #endif + /* If new session or something weird happned */ if (soap_obj == NULL) { - object_init_ex(&tmp_soap, service->soap_class.ce); /* Call constructor */ @@ -1448,25 +1456,23 @@ PHP_METHOD(SoapServer, handle) goto fail; } } -#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION) + /* If session then update session hash with new object */ +#ifdef SOAP_HAS_SESSION_SUPPORT if (service->soap_class.persistence == SOAP_PERSISTENCE_SESSION) { - zval *session_vars = &PS(http_session_vars), *tmp_soap_p; - - ZVAL_DEREF(session_vars); - if (Z_TYPE_P(session_vars) == IS_ARRAY && - (tmp_soap_p = zend_hash_str_update(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1, &tmp_soap)) != NULL) { + zend_string *session_var_name = ZSTR_INIT_LITERAL("_bogus_session_name", false); + zval *tmp_soap_p = php_set_session_var(session_var_name, &tmp_soap, NULL); + if (tmp_soap_p != NULL) { soap_obj = tmp_soap_p; } else { soap_obj = &tmp_soap; } - } else { + zend_string_release_ex(session_var_name, false); + } else +#endif + { soap_obj = &tmp_soap; } -#else - soap_obj = &tmp_soap; -#endif - } function_table = &((Z_OBJCE_P(soap_obj))->function_table); } else { @@ -1534,15 +1540,10 @@ PHP_METHOD(SoapServer, handle) if (service->type == SOAP_CLASS || service->type == SOAP_OBJECT) { call_status = call_user_function(NULL, soap_obj, &function_name, &retval, num_params, params); if (service->type == SOAP_CLASS) { -#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION) if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { zval_ptr_dtor(soap_obj); soap_obj = NULL; } -#else - zval_ptr_dtor(soap_obj); - soap_obj = NULL; -#endif } } else { call_status = call_user_function(EG(function_table), NULL, &function_name, &retval, num_params, params); @@ -1557,11 +1558,7 @@ PHP_METHOD(SoapServer, handle) php_output_discard(); _soap_server_exception(service, function, ZEND_THIS); if (service->type == SOAP_CLASS) { -#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION) if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { -#else - if (soap_obj) { -#endif zval_ptr_dtor(soap_obj); } } @@ -1597,11 +1594,7 @@ PHP_METHOD(SoapServer, handle) php_output_discard(); _soap_server_exception(service, function, ZEND_THIS); if (service->type == SOAP_CLASS) { -#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION) if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) { -#else - if (soap_obj) { -#endif zval_ptr_dtor(soap_obj); } } diff --git a/ext/soap/tests/bugs/gh11941_errors.phpt b/ext/soap/tests/bugs/gh11941_errors.phpt new file mode 100644 index 0000000000000..6d7bb18193674 --- /dev/null +++ b/ext/soap/tests/bugs/gh11941_errors.phpt @@ -0,0 +1,17 @@ +--TEST-- +GH-11941 (soap with session persistence will silently fail when "session" built as a shared object) +--EXTENSIONS-- +soap +--SKIPIF-- + +--FILE-- +"http://testuri.org")); +$server->setPersistence(SOAP_PERSISTENCE_SESSION); +?> +--EXPECTF-- +%aUncaught Error: SoapServer::setPersistence(): Persistence cannot be set when the SOAP server is used in function mode in %s:%d +%a