Skip to content

Commit eb208b5

Browse files
committed
Fix bug GH-11941: soap with session persistence will silently fails when "seession" built as a shared object
This adds an optional dependency on the session extension and adds the necessary APIs to make the functionality work with lazy binding. This can be tested by configuring PHP with `--enable-session=shared` and `--enable-soap=shared` and running the test suite, in particular the buggy behaviour can be observed by the existing test `server009.phpt`.
1 parent f90a32c commit eb208b5

File tree

9 files changed

+78
-38
lines changed

9 files changed

+78
-38
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ PHP NEWS
245245
. Fixed bug #49278 (SoapClient::__getLastResponseHeaders returns NULL if
246246
wsdl operation !has output). (nielsdos)
247247
. Fixed bug #44383 (PHP DateTime not converted to xsd:datetime). (nielsdos)
248+
. Fixed bug GH-11941 (soap with session persistence will silently fail when
249+
"session" built as a shared object). (nielsdos)
248250

249251
- Sockets:
250252
. Removed the deprecated inet_ntoa call support. (David Carlier)

UPGRADING

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,10 @@ PHP 8.4 UPGRADE NOTES
145145
. SoapClient::$typemap is now an array rather than a resource.
146146
Checks using is_resource() (i.e. is_resource($client->typemap)) should be
147147
replaced with checks for null (i.e. $client->typemap !== null).
148+
. The SOAP extension gained an optional dependency on the session extension.
149+
If you build PHP without the session extension and with --enable-rtld-now,
150+
you will experience errors on startup if you also use the SOAP extension.
151+
To solve this, either don't use rtld-now or load the session extension.
148152

149153
- SPL:
150154
. Out of bounds accesses in SplFixedArray now throw an exception of type
@@ -300,6 +304,7 @@ PHP 8.4 UPGRADE NOTES
300304
. Instances of DateTimeInterface that are passed to xsd:datetime or similar
301305
elements are now serialized as such instead of being serialized as an
302306
empty string.
307+
. Session persistence now works with a shared session module.
303308

304309
- XSL:
305310
. It is now possible to use parameters that contain both single and double

UPGRADING.INTERNALS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,12 @@ PHP 8.4 INTERNALS UPGRADE NOTES
253253
Moreover, providing it with a binary safe string is the responsibility of
254254
the caller now.
255255

256+
h. ext/session
257+
- Added the php_get_session_status() API to get the session status, which is
258+
equivalent to reading PS(session_status) but works with shared objects too.
259+
- Added the php_get_session_var_str() API to set a session variable without
260+
needing to create a zend_string.
261+
256262
========================
257263
4. OpCode changes
258264
========================

ext/session/php_session.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,7 @@ PHPAPI zend_result php_session_destroy(void);
256256
PHPAPI void php_add_session_var(zend_string *name);
257257
PHPAPI zval *php_set_session_var(zend_string *name, zval *state_val, php_unserialize_data_t *var_hash);
258258
PHPAPI zval *php_get_session_var(zend_string *name);
259+
PHPAPI zval* php_get_session_var_str(const char *name, size_t name_len);
259260

260261
PHPAPI zend_result php_session_register_module(const ps_module *);
261262

@@ -265,6 +266,7 @@ PHPAPI zend_result php_session_register_serializer(const char *name,
265266

266267
PHPAPI zend_result php_session_start(void);
267268
PHPAPI zend_result php_session_flush(int write);
269+
PHPAPI php_session_status php_get_session_status(void);
268270

269271
PHPAPI const ps_module *_php_find_ps_module(const char *name);
270272
PHPAPI const ps_serializer *_php_find_ps_serializer(const char *name);

ext/session/session.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,14 @@ PHPAPI zval* php_get_session_var(zend_string *name) /* {{{ */
225225
}
226226
/* }}} */
227227

228+
PHPAPI zval* php_get_session_var_str(const char *name, size_t name_len)
229+
{
230+
IF_SESSION_VARS() {
231+
return zend_hash_str_find(Z_ARRVAL_P(Z_REFVAL(PS(http_session_vars))), name, name_len);
232+
}
233+
return NULL;
234+
}
235+
228236
static void php_session_track_init(void) /* {{{ */
229237
{
230238
zval session_vars;
@@ -1632,6 +1640,11 @@ PHPAPI zend_result php_session_flush(int write) /* {{{ */
16321640
}
16331641
/* }}} */
16341642

1643+
PHPAPI php_session_status php_get_session_status(void)
1644+
{
1645+
return PS(session_status);
1646+
}
1647+
16351648
static zend_result php_session_abort(void) /* {{{ */
16361649
{
16371650
if (PS(session_status) == php_session_active) {

ext/soap/config.m4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,5 @@ if test "$PHP_SOAP" != "no"; then
1010
PHP_SUBST(SOAP_SHARED_LIBADD)
1111
])
1212
PHP_ADD_EXTENSION_DEP(soap, libxml)
13+
PHP_ADD_EXTENSION_DEP(soap, session, true)
1314
fi

ext/soap/config.w32

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ if (PHP_SOAP != "no") {
1010
) {
1111
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");
1212
AC_DEFINE('HAVE_SOAP', 1, "SOAP support");
13+
ADD_EXTENSION_DEP('soap', 'session', true);
1314

1415
if (!PHP_SOAP_SHARED) {
1516
ADD_FLAG('CFLAGS_SOAP', "/D LIBXML_STATIC ");

ext/soap/soap.c

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,19 @@
2020
#include "config.h"
2121
#endif
2222
#include "php_soap.h"
23-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
2423
#include "ext/session/php_session.h"
25-
#endif
2624
#include "soap_arginfo.h"
2725
#include "zend_exceptions.h"
2826
#include "zend_interfaces.h"
2927
#include "ext/standard/php_incomplete_class.h"
3028

29+
/* We only have session support if PHP was configured with session support
30+
* or if the session module could be loaded dynamically, which will only
31+
* work if soap is loaded dynamically as well. */
32+
#if defined(HAVE_PHP_SESSION) || defined(COMPILE_DL_SOAP)
33+
# define SOAP_HAS_SESSION_SUPPORT
34+
#endif
35+
3136
typedef struct _soapHeader {
3237
sdlFunctionPtr function;
3338
zval function_name;
@@ -298,6 +303,7 @@ PHP_MINFO_FUNCTION(soap);
298303
static const zend_module_dep soap_deps[] = {
299304
ZEND_MOD_REQUIRED("date")
300305
ZEND_MOD_REQUIRED("libxml")
306+
ZEND_MOD_OPTIONAL("session")
301307
ZEND_MOD_END
302308
};
303309

@@ -995,6 +1001,12 @@ PHP_METHOD(SoapServer, setPersistence)
9951001
if (service->type == SOAP_CLASS) {
9961002
if (value == SOAP_PERSISTENCE_SESSION ||
9971003
value == SOAP_PERSISTENCE_REQUEST) {
1004+
if (value == SOAP_PERSISTENCE_SESSION && !zend_hash_str_exists(&module_registry, "session", sizeof("session")-1)) {
1005+
SOAP_SERVER_END_CODE();
1006+
zend_throw_error(NULL, "SoapServer::setPersistence(): Session persistence cannot be enabled because the session module is not enabled");
1007+
RETURN_THROWS();
1008+
}
1009+
9981010
service->soap_class.persistence = value;
9991011
} else {
10001012
zend_argument_value_error(
@@ -1406,22 +1418,18 @@ PHP_METHOD(SoapServer, handle)
14061418
soap_obj = &service->soap_object;
14071419
function_table = &((Z_OBJCE_P(soap_obj))->function_table);
14081420
} else if (service->type == SOAP_CLASS) {
1409-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
1410-
/* If persistent then set soap_obj from from the previous created session (if available) */
1421+
/* If persistent then set soap_obj from the previous created session (if available) */
1422+
#ifdef SOAP_HAS_SESSION_SUPPORT
14111423
if (service->soap_class.persistence == SOAP_PERSISTENCE_SESSION) {
1412-
zval *session_vars, *tmp_soap_p;
1413-
1414-
if (PS(session_status) != php_session_active &&
1415-
PS(session_status) != php_session_disabled) {
1424+
php_session_status session_status = php_get_session_status();
1425+
if (session_status != php_session_active &&
1426+
session_status != php_session_disabled) {
14161427
php_session_start();
14171428
}
14181429

14191430
/* Find the soap object and assign */
1420-
session_vars = &PS(http_session_vars);
1421-
ZVAL_DEREF(session_vars);
1422-
if (Z_TYPE_P(session_vars) == IS_ARRAY &&
1423-
(tmp_soap_p = zend_hash_str_find(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1)) != NULL &&
1424-
Z_TYPE_P(tmp_soap_p) == IS_OBJECT) {
1431+
zval *tmp_soap_p = php_get_session_var_str("_bogus_session_name", sizeof("_bogus_session_name")-1);
1432+
if (tmp_soap_p != NULL && Z_TYPE_P(tmp_soap_p) == IS_OBJECT) {
14251433
if (EXPECTED(Z_OBJCE_P(tmp_soap_p) == service->soap_class.ce)) {
14261434
soap_obj = tmp_soap_p;
14271435
} else if (Z_OBJCE_P(tmp_soap_p) == php_ce_incomplete_class) {
@@ -1431,9 +1439,9 @@ PHP_METHOD(SoapServer, handle)
14311439
}
14321440
}
14331441
#endif
1442+
14341443
/* If new session or something weird happned */
14351444
if (soap_obj == NULL) {
1436-
14371445
object_init_ex(&tmp_soap, service->soap_class.ce);
14381446

14391447
/* Call constructor */
@@ -1448,25 +1456,23 @@ PHP_METHOD(SoapServer, handle)
14481456
goto fail;
14491457
}
14501458
}
1451-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
1459+
14521460
/* If session then update session hash with new object */
1461+
#ifdef SOAP_HAS_SESSION_SUPPORT
14531462
if (service->soap_class.persistence == SOAP_PERSISTENCE_SESSION) {
1454-
zval *session_vars = &PS(http_session_vars), *tmp_soap_p;
1455-
1456-
ZVAL_DEREF(session_vars);
1457-
if (Z_TYPE_P(session_vars) == IS_ARRAY &&
1458-
(tmp_soap_p = zend_hash_str_update(Z_ARRVAL_P(session_vars), "_bogus_session_name", sizeof("_bogus_session_name")-1, &tmp_soap)) != NULL) {
1463+
zend_string *session_var_name = ZSTR_INIT_LITERAL("_bogus_session_name", false);
1464+
zval *tmp_soap_p = php_set_session_var(session_var_name, &tmp_soap, NULL);
1465+
if (tmp_soap_p != NULL) {
14591466
soap_obj = tmp_soap_p;
14601467
} else {
14611468
soap_obj = &tmp_soap;
14621469
}
1463-
} else {
1470+
zend_string_release_ex(session_var_name, false);
1471+
} else
1472+
#endif
1473+
{
14641474
soap_obj = &tmp_soap;
14651475
}
1466-
#else
1467-
soap_obj = &tmp_soap;
1468-
#endif
1469-
14701476
}
14711477
function_table = &((Z_OBJCE_P(soap_obj))->function_table);
14721478
} else {
@@ -1534,15 +1540,10 @@ PHP_METHOD(SoapServer, handle)
15341540
if (service->type == SOAP_CLASS || service->type == SOAP_OBJECT) {
15351541
call_status = call_user_function(NULL, soap_obj, &function_name, &retval, num_params, params);
15361542
if (service->type == SOAP_CLASS) {
1537-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
15381543
if (service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
15391544
zval_ptr_dtor(soap_obj);
15401545
soap_obj = NULL;
15411546
}
1542-
#else
1543-
zval_ptr_dtor(soap_obj);
1544-
soap_obj = NULL;
1545-
#endif
15461547
}
15471548
} else {
15481549
call_status = call_user_function(EG(function_table), NULL, &function_name, &retval, num_params, params);
@@ -1557,11 +1558,7 @@ PHP_METHOD(SoapServer, handle)
15571558
php_output_discard();
15581559
_soap_server_exception(service, function, ZEND_THIS);
15591560
if (service->type == SOAP_CLASS) {
1560-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
15611561
if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
1562-
#else
1563-
if (soap_obj) {
1564-
#endif
15651562
zval_ptr_dtor(soap_obj);
15661563
}
15671564
}
@@ -1597,11 +1594,7 @@ PHP_METHOD(SoapServer, handle)
15971594
php_output_discard();
15981595
_soap_server_exception(service, function, ZEND_THIS);
15991596
if (service->type == SOAP_CLASS) {
1600-
#if defined(HAVE_PHP_SESSION) && !defined(COMPILE_DL_SESSION)
16011597
if (soap_obj && service->soap_class.persistence != SOAP_PERSISTENCE_SESSION) {
1602-
#else
1603-
if (soap_obj) {
1604-
#endif
16051598
zval_ptr_dtor(soap_obj);
16061599
}
16071600
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
--TEST--
2+
GH-11941 (soap with session persistence will silently fail when "session" built as a shared object)
3+
--EXTENSIONS--
4+
soap
5+
--SKIPIF--
6+
<?php
7+
// We explicitly want to test with the soap extension enabled and session extension disabled
8+
if (extension_loaded("session")) die("skip this test must run with the session extension disabled");
9+
?>
10+
--FILE--
11+
<?php
12+
$server = new SoapServer(null, array('uri'=>"http://testuri.org"));
13+
$server->setPersistence(SOAP_PERSISTENCE_SESSION);
14+
?>
15+
--EXPECTF--
16+
%aUncaught Error: SoapServer::setPersistence(): Persistence cannot be set when the SOAP server is used in function mode in %s:%d
17+
%a

0 commit comments

Comments
 (0)