From 6daf60a64bfa7917029eb25bdbf4fb29b09367a5 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sat, 25 Jul 2026 20:08:46 +0800 Subject: [PATCH 1/5] ext/soap: Fix SOAP classmap validation for integer keys Co-Authored-By: David CARLIER --- NEWS | 2 + UPGRADING | 3 + ext/soap/php_encoding.c | 2 +- ext/soap/soap.c | 17 ++++- ext/soap/tests/classmap_invalid_keys.phpt | 68 +++++++++++++++++++ .../classmap_invalid_keys_error_types.phpt | 32 +++++++++ ...smap_invalid_keys_exceptions_disabled.phpt | 18 +++++ ...lassmap_private_property_packed_array.phpt | 45 ++++++++++++ 8 files changed, 183 insertions(+), 4 deletions(-) create mode 100644 ext/soap/tests/classmap_invalid_keys.phpt create mode 100644 ext/soap/tests/classmap_invalid_keys_error_types.phpt create mode 100644 ext/soap/tests/classmap_invalid_keys_exceptions_disabled.phpt create mode 100644 ext/soap/tests/classmap_private_property_packed_array.phpt diff --git a/NEWS b/NEWS index 484e462698d9..9ee584efb728 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,8 @@ PHP NEWS - SOAP: . Fixed header injection through the Content-Type context option, the soapaction and the cookie names and values. (David Carlier) + . Fixed the SoapClient and SoapServer "classmap" option to reject arrays + containing integer keys. (Weilin Du, David Carlier) - MBString: . Fixed bug GH-22779 (mb_strrpos() returns the wrong position for a negative diff --git a/UPGRADING b/UPGRADING index 93245f87f42a..494bc2208e6c 100644 --- a/UPGRADING +++ b/UPGRADING @@ -155,6 +155,9 @@ PHP 8.6 UPGRADE NOTES system. - SOAP: + . The "classmap" option of SoapClient and SoapServer now rejects arrays + containing integer keys. Previously, sparse integer-keyed and mixed-keyed + arrays could be accepted. . WSDL/XML Schema parsing now rejects out-of-range integer values for occurrence constraints and integer restriction facets. Negative minOccurs and maxOccurs values are rejected as well. diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index d08c5a683189..72de145e7b04 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -440,7 +440,7 @@ static xmlNodePtr master_to_xml_int(encodePtr encode, zval *data, int style, xml zval *tmp; zend_string *type_name; - ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(SOAP_GLOBAL(class_map), type_name, tmp) { + ZEND_HASH_FOREACH_STR_KEY_VAL(SOAP_GLOBAL(class_map), type_name, tmp) { ZVAL_DEREF(tmp); if (Z_TYPE_P(tmp) == IS_STRING && ZSTR_LEN(ce->name) == Z_STRLEN_P(tmp) && diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 171b529b3355..0edb5fcfae13 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -930,6 +930,18 @@ static HashTable* soap_create_typemap(sdlPtr sdl, HashTable *ht) /* {{{ */ } /* }}} */ +static bool soap_class_map_has_only_string_keys(const HashTable *class_map) +{ + zend_string *key; + ZEND_HASH_FOREACH_STR_KEY(class_map, key) { + if (UNEXPECTED(key == NULL)) { + return false; + } + } ZEND_HASH_FOREACH_END(); + + return true; +} + /* {{{ SoapServer constructor */ PHP_METHOD(SoapServer, __construct) { @@ -1007,8 +1019,7 @@ PHP_METHOD(SoapServer, __construct) zend_argument_type_error(2, "\"classmap\" option must be of type array, %s given", zend_zval_type_name(class_map_zv)); goto cleanup; } - // TODO: this still accepts mixed keys arrays and not all numerically indexed arrays are packed - if (UNEXPECTED(HT_IS_PACKED(Z_ARRVAL_P(class_map_zv)))) { + if (UNEXPECTED(!soap_class_map_has_only_string_keys(Z_ARRVAL_P(class_map_zv)))) { zend_argument_value_error(2, "\"classmap\" option must be an associative array"); goto cleanup; } @@ -2233,7 +2244,7 @@ PHP_METHOD(SoapClient, __construct) } if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY) { - if (UNEXPECTED(HT_IS_PACKED(Z_ARRVAL_P(tmp)))) { + if (UNEXPECTED(!soap_class_map_has_only_string_keys(Z_ARRVAL_P(tmp)))) { php_error_docref(NULL, E_ERROR, "'classmap' option must be an associative array"); } ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); diff --git a/ext/soap/tests/classmap_invalid_keys.phpt b/ext/soap/tests/classmap_invalid_keys.phpt new file mode 100644 index 000000000000..681a59a5fc51 --- /dev/null +++ b/ext/soap/tests/classmap_invalid_keys.phpt @@ -0,0 +1,68 @@ +--TEST-- +SoapClient and SoapServer classmap options must only contain string keys +--EXTENSIONS-- +soap +--FILE-- + 'stdClass']; +unset($emptyHash['type']); + +$cases = [ + 'empty' => [], + 'empty hash' => $emptyHash, + 'packed' => ['stdClass'], + 'sparse numeric' => [100 => 'stdClass'], + 'numeric string' => ['1' => 'stdClass'], + 'mixed' => ['type' => 'stdClass', 1 => 'stdClass'], + 'associative' => ['type' => 'stdClass'], +]; + +foreach ($cases as $name => $classmap) { + echo "-- $name --\n"; + + try { + new SoapClient(null, [ + 'location' => 'http://example.com/', + 'uri' => 'urn:test', + 'classmap' => $classmap, + ]); + echo "SoapClient: OK\n"; + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + } + + try { + new SoapServer(null, [ + 'uri' => 'urn:test', + 'classmap' => $classmap, + ]); + echo "SoapServer: OK\n"; + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + } +} + +?> +--EXPECT-- +-- empty -- +SoapClient: OK +SoapServer: OK +-- empty hash -- +SoapClient: OK +SoapServer: OK +-- packed -- +SoapClient::__construct(): 'classmap' option must be an associative array +SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array +-- sparse numeric -- +SoapClient::__construct(): 'classmap' option must be an associative array +SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array +-- numeric string -- +SoapClient::__construct(): 'classmap' option must be an associative array +SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array +-- mixed -- +SoapClient::__construct(): 'classmap' option must be an associative array +SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array +-- associative -- +SoapClient: OK +SoapServer: OK diff --git a/ext/soap/tests/classmap_invalid_keys_error_types.phpt b/ext/soap/tests/classmap_invalid_keys_error_types.phpt new file mode 100644 index 000000000000..1eb190bdbfff --- /dev/null +++ b/ext/soap/tests/classmap_invalid_keys_error_types.phpt @@ -0,0 +1,32 @@ +--TEST-- +SoapClient and SoapServer report an invalid classmap option differently +--EXTENSIONS-- +soap +--FILE-- + 'stdClass', 1 => 'stdClass']; + +try { + new SoapClient(null, [ + 'location' => 'http://example.com/', + 'uri' => 'urn:test', + 'classmap' => $classmap, + ]); +} catch (Throwable $e) { + echo 'SoapClient: ', $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +try { + new SoapServer(null, [ + 'uri' => 'urn:test', + 'classmap' => $classmap, + ]); +} catch (Throwable $e) { + echo 'SoapServer: ', $e::class, ': ', $e->getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +SoapClient: SoapFault: SoapClient::__construct(): 'classmap' option must be an associative array +SoapServer: ValueError: SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array diff --git a/ext/soap/tests/classmap_invalid_keys_exceptions_disabled.phpt b/ext/soap/tests/classmap_invalid_keys_exceptions_disabled.phpt new file mode 100644 index 000000000000..0d80b132e3c5 --- /dev/null +++ b/ext/soap/tests/classmap_invalid_keys_exceptions_disabled.phpt @@ -0,0 +1,18 @@ +--TEST-- +SoapClient reports an invalid classmap option as a fatal error when exceptions are disabled +--EXTENSIONS-- +soap +--FILE-- + 'http://example.com/', + 'uri' => 'urn:test', + 'exceptions' => false, + 'classmap' => ['type' => 'stdClass', 1 => 'stdClass'], +]); +echo 'not reached', PHP_EOL; + +?> +--EXPECTF-- +Fatal error: SoapClient::__construct(): 'classmap' option must be an associative array in %s on line %d diff --git a/ext/soap/tests/classmap_private_property_packed_array.phpt b/ext/soap/tests/classmap_private_property_packed_array.phpt new file mode 100644 index 000000000000..058483d2dc27 --- /dev/null +++ b/ext/soap/tests/classmap_private_property_packed_array.phpt @@ -0,0 +1,45 @@ +--TEST-- +SoapClient must not read packed private classmap as string-keyed map +--EXTENSIONS-- +soap +--FILE-- + + + + + SOAP-ENV:Server + expected fault + + + +XML; + } +} + +class Foo {} + +$client = new LocalSoapClient(null, [ + 'location' => 'http://example.org/', + 'uri' => 'http://example.org/', +]); + +$property = new ReflectionProperty(SoapClient::class, '_classmap'); +$property->setValue($client, ['Foo']); + +try { + $client->__soapCall('foo', [new Foo()]); +} catch (SoapFault) { + echo "SOAP Fault thrown\n"; +} + +?> +--EXPECT-- +__doRequest called +SOAP Fault thrown From b49aaf49fade348f8173005e086e02d10deb2167 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 26 Jul 2026 02:47:05 +0800 Subject: [PATCH 2/5] Change to ValueError --- NEWS | 3 ++- UPGRADING | 3 ++- ext/soap/soap.c | 8 ++++++- ext/soap/tests/bugs/gh16256.phpt | 2 +- ext/soap/tests/classmap_invalid_keys.phpt | 8 +++---- .../classmap_invalid_keys_error_types.phpt | 4 ++-- ...smap_invalid_keys_exceptions_disabled.phpt | 23 +++++++++++-------- 7 files changed, 31 insertions(+), 20 deletions(-) diff --git a/NEWS b/NEWS index 9ee584efb728..c0d1fe3f4647 100644 --- a/NEWS +++ b/NEWS @@ -52,7 +52,8 @@ PHP NEWS . Fixed header injection through the Content-Type context option, the soapaction and the cookie names and values. (David Carlier) . Fixed the SoapClient and SoapServer "classmap" option to reject arrays - containing integer keys. (Weilin Du, David Carlier) + containing integer keys, and made SoapClient throw ValueError for invalid + "classmap" options. (Weilin Du, David Carlier) - MBString: . Fixed bug GH-22779 (mb_strrpos() returns the wrong position for a negative diff --git a/UPGRADING b/UPGRADING index 494bc2208e6c..e3a7c888db0f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -157,7 +157,8 @@ PHP 8.6 UPGRADE NOTES - SOAP: . The "classmap" option of SoapClient and SoapServer now rejects arrays containing integer keys. Previously, sparse integer-keyed and mixed-keyed - arrays could be accepted. + arrays could be accepted. SoapClient now throws a ValueError for invalid + "classmap" options, also when the "exceptions" option is disabled. . WSDL/XML Schema parsing now rejects out-of-range integer values for occurrence constraints and integer restriction facets. Negative minOccurs and maxOccurs values are rejected as well. diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 0edb5fcfae13..635d465f5155 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2245,7 +2245,11 @@ PHP_METHOD(SoapClient, __construct) if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY) { if (UNEXPECTED(!soap_class_map_has_only_string_keys(Z_ARRVAL_P(tmp)))) { - php_error_docref(NULL, E_ERROR, "'classmap' option must be an associative array"); + zend_argument_value_error(2, "\"classmap\" option must be an associative array"); + if (context) { + zend_list_delete(context->res); + } + goto finish; } ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); } @@ -2324,6 +2328,8 @@ PHP_METHOD(SoapClient, __construct) if (typemap_ht) { soap_client_object_fetch(Z_OBJ_P(this_ptr))->typemap = soap_create_typemap(sdl, typemap_ht); } + +finish: SOAP_CLIENT_END_CODE(); } /* }}} */ diff --git a/ext/soap/tests/bugs/gh16256.phpt b/ext/soap/tests/bugs/gh16256.phpt index ce2ba1314069..a6d5f3fbbf3c 100644 --- a/ext/soap/tests/bugs/gh16256.phpt +++ b/ext/soap/tests/bugs/gh16256.phpt @@ -20,5 +20,5 @@ try { } ?> --EXPECT-- -SoapClient::__construct(): 'classmap' option must be an associative array +SoapClient::__construct(): Argument #2 ($options) "classmap" option must be an associative array SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array diff --git a/ext/soap/tests/classmap_invalid_keys.phpt b/ext/soap/tests/classmap_invalid_keys.phpt index 681a59a5fc51..97b815dff229 100644 --- a/ext/soap/tests/classmap_invalid_keys.phpt +++ b/ext/soap/tests/classmap_invalid_keys.phpt @@ -52,16 +52,16 @@ SoapServer: OK SoapClient: OK SoapServer: OK -- packed -- -SoapClient::__construct(): 'classmap' option must be an associative array +SoapClient::__construct(): Argument #2 ($options) "classmap" option must be an associative array SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array -- sparse numeric -- -SoapClient::__construct(): 'classmap' option must be an associative array +SoapClient::__construct(): Argument #2 ($options) "classmap" option must be an associative array SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array -- numeric string -- -SoapClient::__construct(): 'classmap' option must be an associative array +SoapClient::__construct(): Argument #2 ($options) "classmap" option must be an associative array SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array -- mixed -- -SoapClient::__construct(): 'classmap' option must be an associative array +SoapClient::__construct(): Argument #2 ($options) "classmap" option must be an associative array SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array -- associative -- SoapClient: OK diff --git a/ext/soap/tests/classmap_invalid_keys_error_types.phpt b/ext/soap/tests/classmap_invalid_keys_error_types.phpt index 1eb190bdbfff..57b73f09d72e 100644 --- a/ext/soap/tests/classmap_invalid_keys_error_types.phpt +++ b/ext/soap/tests/classmap_invalid_keys_error_types.phpt @@ -1,5 +1,5 @@ --TEST-- -SoapClient and SoapServer report an invalid classmap option differently +SoapClient and SoapServer report an invalid classmap option as ValueError --EXTENSIONS-- soap --FILE-- @@ -28,5 +28,5 @@ try { ?> --EXPECT-- -SoapClient: SoapFault: SoapClient::__construct(): 'classmap' option must be an associative array +SoapClient: ValueError: SoapClient::__construct(): Argument #2 ($options) "classmap" option must be an associative array SoapServer: ValueError: SoapServer::__construct(): Argument #2 ($options) "classmap" option must be an associative array diff --git a/ext/soap/tests/classmap_invalid_keys_exceptions_disabled.phpt b/ext/soap/tests/classmap_invalid_keys_exceptions_disabled.phpt index 0d80b132e3c5..8340a5dbf78c 100644 --- a/ext/soap/tests/classmap_invalid_keys_exceptions_disabled.phpt +++ b/ext/soap/tests/classmap_invalid_keys_exceptions_disabled.phpt @@ -1,18 +1,21 @@ --TEST-- -SoapClient reports an invalid classmap option as a fatal error when exceptions are disabled +SoapClient reports an invalid classmap option as ValueError when exceptions are disabled --EXTENSIONS-- soap --FILE-- 'http://example.com/', - 'uri' => 'urn:test', - 'exceptions' => false, - 'classmap' => ['type' => 'stdClass', 1 => 'stdClass'], -]); -echo 'not reached', PHP_EOL; +try { + new SoapClient(null, [ + 'location' => 'http://example.com/', + 'uri' => 'urn:test', + 'exceptions' => false, + 'classmap' => ['type' => 'stdClass', 1 => 'stdClass'], + ]); +} catch (Throwable $e) { + echo $e::class, ': ', $e->getMessage(), PHP_EOL; +} ?> ---EXPECTF-- -Fatal error: SoapClient::__construct(): 'classmap' option must be an associative array in %s on line %d +--EXPECT-- +ValueError: SoapClient::__construct(): Argument #2 ($options) "classmap" option must be an associative array From a2232d43bb38b39b891143bc0b0af97cca595508 Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 26 Jul 2026 03:11:09 +0800 Subject: [PATCH 3/5] fix CI --- ext/soap/soap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 635d465f5155..cd02f2d01de8 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2330,6 +2330,7 @@ PHP_METHOD(SoapClient, __construct) } finish: + ; SOAP_CLIENT_END_CODE(); } /* }}} */ From b92b3762f820ed639c4d0297de3d8b3f0cdd895d Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 26 Jul 2026 20:45:58 +0800 Subject: [PATCH 4/5] Reject invalid classmap earlier --- ext/soap/soap.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index cd02f2d01de8..274eedd48b04 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2145,6 +2145,15 @@ PHP_METHOD(SoapClient, __construct) } } + if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && + Z_TYPE_P(tmp) == IS_ARRAY) { + if (UNEXPECTED(!soap_class_map_has_only_string_keys(Z_ARRVAL_P(tmp)))) { + zend_argument_value_error(2, "\"classmap\" option must be an associative array"); + goto finish; + } + ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); + } + if ((tmp = zend_hash_str_find(ht, "stream_context", sizeof("stream_context")-1)) != NULL && Z_TYPE_P(tmp) == IS_RESOURCE) { context = php_stream_context_from_zval(tmp, 1); @@ -2242,18 +2251,6 @@ PHP_METHOD(SoapClient, __construct) ZVAL_STR_COPY(Z_CLIENT_ENCODING_P(this_ptr), Z_STR_P(tmp)); } } - if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && - Z_TYPE_P(tmp) == IS_ARRAY) { - if (UNEXPECTED(!soap_class_map_has_only_string_keys(Z_ARRVAL_P(tmp)))) { - zend_argument_value_error(2, "\"classmap\" option must be an associative array"); - if (context) { - zend_list_delete(context->res); - } - goto finish; - } - ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); - } - if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(tmp)) > 0) { From 2c3a3d81c859f9d029fee8895f359f68df62825e Mon Sep 17 00:00:00 2001 From: Weilin Du Date: Sun, 26 Jul 2026 22:06:56 +0800 Subject: [PATCH 5/5] Update soap.c --- ext/soap/soap.c | 57 +++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 274eedd48b04..305368f0041c 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -2145,23 +2145,6 @@ PHP_METHOD(SoapClient, __construct) } } - if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && - Z_TYPE_P(tmp) == IS_ARRAY) { - if (UNEXPECTED(!soap_class_map_has_only_string_keys(Z_ARRVAL_P(tmp)))) { - zend_argument_value_error(2, "\"classmap\" option must be an associative array"); - goto finish; - } - ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); - } - - if ((tmp = zend_hash_str_find(ht, "stream_context", sizeof("stream_context")-1)) != NULL && - Z_TYPE_P(tmp) == IS_RESOURCE) { - context = php_stream_context_from_zval(tmp, 1); - Z_ADDREF_P(tmp); - } else { - context = php_stream_context_alloc(); - } - if ((tmp = zend_hash_str_find(ht, "location", sizeof("location")-1)) != NULL && Z_TYPE_P(tmp) == IS_STRING) { ZVAL_STR_COPY(Z_CLIENT_LOCATION_P(this_ptr), Z_STR_P(tmp)); @@ -2207,17 +2190,6 @@ PHP_METHOD(SoapClient, __construct) } } } - if ((tmp = zend_hash_str_find(ht, "local_cert", sizeof("local_cert")-1)) != NULL && - Z_TYPE_P(tmp) == IS_STRING) { - if (!context) { - context = php_stream_context_alloc(); - } - php_stream_context_set_option(context, "ssl", "local_cert", tmp); - if ((tmp = zend_hash_str_find(ht, "passphrase", sizeof("passphrase")-1)) != NULL && - Z_TYPE_P(tmp) == IS_STRING) { - php_stream_context_set_option(context, "ssl", "passphrase", tmp); - } - } if ((tmp = zend_hash_find(ht, ZSTR_KNOWN(ZEND_STR_TRACE))) != NULL && (Z_TYPE_P(tmp) == IS_TRUE || (Z_TYPE_P(tmp) == IS_LONG && Z_LVAL_P(tmp) == 1))) { @@ -2251,6 +2223,35 @@ PHP_METHOD(SoapClient, __construct) ZVAL_STR_COPY(Z_CLIENT_ENCODING_P(this_ptr), Z_STR_P(tmp)); } } + if ((tmp = zend_hash_str_find(ht, "classmap", sizeof("classmap")-1)) != NULL && + Z_TYPE_P(tmp) == IS_ARRAY) { + if (UNEXPECTED(!soap_class_map_has_only_string_keys(Z_ARRVAL_P(tmp)))) { + zend_argument_value_error(2, "\"classmap\" option must be an associative array"); + goto finish; + } + ZVAL_COPY(Z_CLIENT_CLASSMAP_P(this_ptr), tmp); + } + + if ((tmp = zend_hash_str_find(ht, "stream_context", sizeof("stream_context")-1)) != NULL && + Z_TYPE_P(tmp) == IS_RESOURCE) { + context = php_stream_context_from_zval(tmp, 1); + Z_ADDREF_P(tmp); + } else { + context = php_stream_context_alloc(); + } + + if ((tmp = zend_hash_str_find(ht, "local_cert", sizeof("local_cert")-1)) != NULL && + Z_TYPE_P(tmp) == IS_STRING) { + if (!context) { + context = php_stream_context_alloc(); + } + php_stream_context_set_option(context, "ssl", "local_cert", tmp); + if ((tmp = zend_hash_str_find(ht, "passphrase", sizeof("passphrase")-1)) != NULL && + Z_TYPE_P(tmp) == IS_STRING) { + php_stream_context_set_option(context, "ssl", "passphrase", tmp); + } + } + if ((tmp = zend_hash_str_find(ht, "typemap", sizeof("typemap")-1)) != NULL && Z_TYPE_P(tmp) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(tmp)) > 0) {