diff --git a/NEWS b/NEWS index e112bfb5351e..89a8dd8dc939 100644 --- a/NEWS +++ b/NEWS @@ -6,7 +6,14 @@ PHP NEWS . Fixed bug GH-23242 (PHP development server does not support Expect 100-continue flow control). (Sjoerd Langkemper) +- DOM: + . Fixed stale getElementsByClassName() and other node list caches after + className/classList writes and attribute removals. (Ilia Alshanetsky) + - Intl: + . Fixed a memory leak when dumping IntlCalendar instances. (Ilia Alshanetsky) + . Fixed Collator::sortWithSortKeys() allocating fixed 2MiB buffers + regardless of array size. (Ilia Alshanetsky) . Fixed a memory leak when iterating IntlBreakIterator::getPartsIterator() results. (iliaal) . Fixed a leak in Locale::getKeywords() when a keyword value cannot be @@ -28,6 +35,15 @@ PHP NEWS . Fixed bug GH-23477 (Memory leak on duplicate native Phar manifest entries). (Weilin Du) +- Sockets: + . Fixed socket_set_option() with SO_ATTACH_REUSEPORT_CBPF and a zero value, + which detached the classic BPF filter instead of the reuseport program. + (David Carlier) + +- SOAP: + . Fixed WSDL cache corruption when a soap:header defines headerfaults. + (Ilia Alshanetsky) + - Standard: . Fixed a segfault when a stream filter callback unsets StreamBucket::$data before re-attaching the bucket. (iliaal) @@ -37,6 +53,10 @@ PHP NEWS . Io\Poll\Context::wait() now rejects a $maxEvents value greater than INT_MAX instead of truncating it. (marc-mabe) +- SimpleXML: + . Fixed writing to a dimension of the object returned by attributes() not + creating the attribute. (Ilia Alshanetsky) + 27 Aug 2026, PHP 8.6.0beta2 diff --git a/UPGRADING b/UPGRADING index 74ecf90408aa..5a5cafc0234f 100644 --- a/UPGRADING +++ b/UPGRADING @@ -199,6 +199,16 @@ PHP 8.6 UPGRADE NOTES rules" message. Code that compares the exact message may need to be updated. +- Sockets: + . socket_set_option() with SO_ATTACH_REUSEPORT_CBPF now requires an int + $value and a $level of SOL_SOCKET. Any other value type throws a TypeError + instead of being coerced, and any other level raises a warning and returns + false. + . socket_set_option() with SO_ATTACH_REUSEPORT_CBPF and a $value of 0 now + detaches the reuseport filter through SO_DETACH_REUSEPORT_BPF. It + previously used SO_DETACH_BPF, an alias of SO_DETACH_FILTER, which left the + reuseport program attached. + - Sodium: . The password-hashing functions sodium_crypto_pwhash(), sodium_crypto_pwhash_str(), @@ -484,10 +494,10 @@ PHP 8.6 UPGRADE NOTES RFC: https://wiki.php.net/rfc/uri_followup#uri_type_detection . Added Uri\Rfc3986\Uri::getHostType() and Uri\WhatWg\Url::getHostType(). RFC: https://wiki.php.net/rfc/uri_followup#host_type_detection - . Added Uri\Rfc3986\UriBuilder. - RFC: https://wiki.php.net/rfc/uri_followup#uri_building - . Added Uri\WhatWg\UrlBuilder. + . Added Uri\Rfc3986\UriBuilder and Uri\WhatWg\UrlBuilder. RFC: https://wiki.php.net/rfc/uri_followup#uri_building + . Added Uri\url_percent_encode(). + RFC: https://wiki.php.net/rfc/uri_followup#percent-encoding_support ======================================== 3. Changes in SAPI modules @@ -860,6 +870,7 @@ PHP 8.6 UPGRADE NOTES . EAI_ALLDONE. . EAI_INTR. . EAI_IDN_ENCODE. + . SO_DETACH_REUSEPORT_BPF (Linux only). - Standard: . ARRAY_FILTER_USE_VALUE. @@ -962,6 +973,7 @@ PHP 8.6 UPGRADE NOTES . Reduced temporary allocations when iterating Phar directories. - Standard: + . Improved performance of str_ends_with(). . Improved performance of array_fill_keys(). . Improved performance of array_intersect(). . Improved performance of array_map() with multiple arrays passed. diff --git a/ext/dom/element.c b/ext/dom/element.c index 354466623ca4..5fcffaf42055 100644 --- a/ext/dom/element.c +++ b/ext/dom/element.c @@ -154,6 +154,7 @@ static xmlAttrPtr dom_element_reflected_attribute_write(dom_object *obj, zval *n /* Typed property, so it is a string already */ ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING); + php_libxml_invalidate_node_list_cache(obj->document); return xmlSetNsProp(nodep, NULL, (const xmlChar *) name, (const xmlChar *) Z_STRVAL_P(newval)); } @@ -542,7 +543,7 @@ static void dom_deep_ns_redef(xmlNodePtr node, xmlNsPtr ns_to_redefine) efree(worklist); } -static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp) +static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp, php_libxml_ref_obj *document) { ZEND_ASSERT(thisp != NULL); ZEND_ASSERT(attrp != NULL); @@ -597,6 +598,7 @@ static bool dom_remove_attribute(xmlNodePtr thisp, xmlNodePtr attrp) return false; default: ZEND_UNREACHABLE(); } + php_libxml_invalidate_node_list_cache(document); return true; } @@ -622,7 +624,7 @@ PHP_METHOD(DOMElement, removeAttribute) RETURN_FALSE; } - RETURN_BOOL(dom_remove_attribute(nodep, attrp)); + RETURN_BOOL(dom_remove_attribute(nodep, attrp, intern->document)); } PHP_METHOD(Dom_Element, removeAttribute) @@ -640,7 +642,7 @@ PHP_METHOD(Dom_Element, removeAttribute) attrp = dom_get_attribute_or_nsdecl(intern, nodep, BAD_CAST name, name_len); if (attrp != NULL) { - dom_remove_attribute(nodep, attrp); + dom_remove_attribute(nodep, attrp, intern->document); } } /* }}} end dom_element_remove_attribute */ @@ -798,6 +800,7 @@ static void dom_element_remove_attribute_node(INTERNAL_FUNCTION_PARAMETERS, zend RETURN_FALSE; } + php_libxml_invalidate_node_list_cache(intern->document); xmlUnlinkNode((xmlNodePtr) attrp); DOM_RET_OBJ((xmlNodePtr) attrp, intern); @@ -1198,6 +1201,7 @@ PHP_METHOD(DOMElement, removeAttributeNS) if (nsptr != NULL) { if (xmlStrEqual(BAD_CAST uri, nsptr->href)) { dom_eliminate_ns(nodep, nsptr); + php_libxml_invalidate_node_list_cache(intern->document); } else { return; } @@ -1212,6 +1216,7 @@ PHP_METHOD(DOMElement, removeAttributeNS) } else { xmlUnlinkNode((xmlNodePtr) attrp); } + php_libxml_invalidate_node_list_cache(intern->document); } } /* }}} end dom_element_remove_attribute_ns */ @@ -1947,7 +1952,7 @@ PHP_METHOD(DOMElement, toggleAttribute) /* Step 5 */ if (force_is_null || !force) { - retval = !dom_remove_attribute(thisp, attribute); + retval = !dom_remove_attribute(thisp, attribute, intern->document); goto out; } diff --git a/ext/dom/tests/modern/common/getElementsByClassName_cache_invalidation.phpt b/ext/dom/tests/modern/common/getElementsByClassName_cache_invalidation.phpt new file mode 100644 index 000000000000..4efdad1b59b4 --- /dev/null +++ b/ext/dom/tests/modern/common/getElementsByClassName_cache_invalidation.phpt @@ -0,0 +1,44 @@ +--TEST-- +getElementsByClassName() cache must be invalidated by class attribute mutations +--EXTENSIONS-- +dom +--FILE-- +$body"); +} + +$checks = [ + 'className' => function ($doc, $span) { $span->className = 'zzz'; }, + 'classList-remove' => function ($doc, $span) { $span->classList->remove('foo'); }, + 'classList-value' => function ($doc, $span) { $span->classList->value = 'zzz'; }, + 'setAttribute' => function ($doc, $span) { $span->setAttribute('class', 'zzz'); }, + 'removeAttribute' => function ($doc, $span) { $span->removeAttribute('class'); }, + 'removeAttributeNode' => function ($doc, $span) { $span->removeAttributeNode($span->attributes['class']); }, +]; +foreach ($checks as $label => $fn) { + $doc = mk(''); + $coll = $doc->getElementsByClassName('foo'); + if ($coll->length !== 1) { + echo "$label: unexpected initial length\n"; + continue; + } + $fn($doc, $doc->querySelector('span')); + echo "$label: ", $coll->length === 0 ? "OK" : "STALE {$coll->length}", "\n"; +} + +$doc = mk(''); +$coll = $doc->getElementsByClassName('foo'); +var_dump($coll->length); +$doc->querySelector('span')->className = 'foo'; +echo $coll->length === 1 ? "growth OK" : "growth STALE", "\n"; +?> +--EXPECT-- +className: OK +classList-remove: OK +classList-value: OK +setAttribute: OK +removeAttribute: OK +removeAttributeNode: OK +int(0) +growth OK diff --git a/ext/dom/token_list.c b/ext/dom/token_list.c index 0e7797616554..30f308a14d67 100644 --- a/ext/dom/token_list.c +++ b/ext/dom/token_list.c @@ -182,6 +182,7 @@ static void dom_token_list_update(dom_token_list_object *intern) HashTable *token_set = TOKEN_LIST_GET_SET(intern); php_libxml_invalidate_cache_tag(&intern->cache_tag); + php_libxml_invalidate_node_list_cache(intern->dom.document); /* 1. If the associated element does not have an associated attribute and token set is empty, then return. */ if (attr == NULL && zend_hash_num_elements(token_set) == 0) { @@ -430,6 +431,7 @@ zend_result dom_token_list_value_write(dom_object *obj, zval *newval) zend_value_error("Value must not contain any null bytes"); return FAILURE; } + php_libxml_invalidate_node_list_cache(intern->dom.document); xmlSetNsProp(dom_token_list_get_element(intern), NULL, BAD_CAST "class", BAD_CAST Z_STRVAL_P(newval)); /* Note: we don't update the set here, the set is always lazily updated for performance reasons. */ return SUCCESS; diff --git a/ext/intl/calendar/calendar_class.cpp b/ext/intl/calendar/calendar_class.cpp index c74f0ab9412f..46e8ba49b89c 100644 --- a/ext/intl/calendar/calendar_class.cpp +++ b/ext/intl/calendar/calendar_class.cpp @@ -171,6 +171,8 @@ static HashTable *Calendar_get_debug_info(zend_object *object, int *is_temp) FREE_HASHTABLE(debug_info_tz); zend_hash_str_update(debug_info, "timeZone", sizeof("timeZone") - 1, &ztz_debug); + + zval_ptr_dtor(&ztz); } { diff --git a/ext/intl/collator/collator_sort.cpp b/ext/intl/collator/collator_sort.cpp index cb1f2aefc358..f2674b9c8ff8 100644 --- a/ext/intl/collator/collator_sort.cpp +++ b/ext/intl/collator/collator_sort.cpp @@ -44,9 +44,8 @@ ZEND_EXTERN_MODULE_GLOBALS( intl ) static const size_t DEF_SORT_KEYS_BUF_SIZE = 1048576; static const size_t DEF_SORT_KEYS_BUF_INCREMENT = 1048576; - -static const size_t DEF_SORT_KEYS_INDX_BUF_SIZE = 1048576; -static const size_t DEF_SORT_KEYS_INDX_BUF_INCREMENT = 1048576; +static const size_t MIN_SORT_KEYS_BUF_SIZE = 4096; +static const size_t SORT_KEY_LENGTH_ESTIMATE = 32; static const size_t DEF_UTF16_BUF_SIZE = 1024; @@ -427,17 +426,17 @@ U_CFUNC PHP_FUNCTION( collator_sort_with_sort_keys ) zval* hashData = nullptr; /* currently processed item of input hash */ char* sortKeyBuf = nullptr; /* buffer to store sort keys */ - uint32_t sortKeyBufSize = DEF_SORT_KEYS_BUF_SIZE; /* buffer size */ + uint32_t sortKeyBufSize = 0; /* buffer size */ ptrdiff_t sortKeyBufOffset = 0; /* pos in buffer to store sort key */ uint32_t sortKeyLen = 0; /* the length of currently processing key */ uint32_t bufLeft = 0; uint32_t bufIncrement = 0; collator_sort_key_index_t* sortKeyIndxBuf = nullptr; /* buffer to store 'indexes' which will be passed to 'qsort' */ - uint32_t sortKeyIndxBufSize = DEF_SORT_KEYS_INDX_BUF_SIZE; uint32_t sortKeyIndxSize = sizeof( collator_sort_key_index_t ); uint32_t sortKeyCount = 0; + uint32_t numElements = 0; uint32_t j = 0; UChar* utf16_buf = nullptr; /* tmp buffer to hold current processing string in utf-16 */ @@ -472,9 +471,20 @@ U_CFUNC PHP_FUNCTION( collator_sort_with_sort_keys ) if( !hash || zend_hash_num_elements( hash ) == 0 ) RETURN_TRUE; + numElements = zend_hash_num_elements( hash ); + + if( numElements > DEF_SORT_KEYS_BUF_SIZE / SORT_KEY_LENGTH_ESTIMATE ) { + sortKeyBufSize = DEF_SORT_KEYS_BUF_SIZE; + } else { + sortKeyBufSize = numElements * SORT_KEY_LENGTH_ESTIMATE; + } + if( sortKeyBufSize < MIN_SORT_KEYS_BUF_SIZE ) { + sortKeyBufSize = MIN_SORT_KEYS_BUF_SIZE; + } + /* Create buffers */ - sortKeyBuf = reinterpret_cast(ecalloc( sortKeyBufSize, sizeof( char ) )); - sortKeyIndxBuf = reinterpret_cast(ecalloc( sortKeyIndxBufSize, sizeof( uint8_t ) )); + sortKeyBuf = reinterpret_cast(ecalloc( sortKeyBufSize, sizeof( char ) )); + sortKeyIndxBuf = reinterpret_cast(ecalloc( numElements, sortKeyIndxSize )); utf16_buf = eumalloc( utf16_buf_size ); /* Iterate through input hash and create a sort key for each value. */ @@ -524,7 +534,15 @@ U_CFUNC PHP_FUNCTION( collator_sort_with_sort_keys ) /* check for sortKeyBuf overflow, increasing its size of the buffer if needed */ if( sortKeyLen > bufLeft ) { - bufIncrement = ( sortKeyLen > DEF_SORT_KEYS_BUF_INCREMENT ) ? sortKeyLen : DEF_SORT_KEYS_BUF_INCREMENT; + bufIncrement = sortKeyBufSize; + + if( bufIncrement > DEF_SORT_KEYS_BUF_INCREMENT ) { + bufIncrement = DEF_SORT_KEYS_BUF_INCREMENT; + } + + if( bufIncrement < sortKeyLen ) { + bufIncrement = sortKeyLen; + } sortKeyBufSize += bufIncrement; bufLeft += bufIncrement; @@ -534,16 +552,6 @@ U_CFUNC PHP_FUNCTION( collator_sort_with_sort_keys ) sortKeyLen = ucol_getSortKey( co->ucoll, utf16_buf, utf16_len, (uint8_t*)sortKeyBuf + sortKeyBufOffset, bufLeft ); } - /* check sortKeyIndxBuf overflow, increasing its size of the buffer if needed */ - if( ( sortKeyCount + 1 ) * sortKeyIndxSize > sortKeyIndxBufSize ) - { - bufIncrement = ( sortKeyIndxSize > DEF_SORT_KEYS_INDX_BUF_INCREMENT ) ? sortKeyIndxSize : DEF_SORT_KEYS_INDX_BUF_INCREMENT; - - sortKeyIndxBufSize += bufIncrement; - - sortKeyIndxBuf = reinterpret_cast(erealloc( sortKeyIndxBuf, sortKeyIndxBufSize )); - } - sortKeyIndxBuf[sortKeyCount].key = (char*)sortKeyBufOffset; /* remember just offset, cause address */ /* of 'sortKeyBuf' may be changed due to realloc. */ sortKeyIndxBuf[sortKeyCount].zstr = hashData; diff --git a/ext/intl/tests/calendar_get_debug_info_tz_leak.phpt b/ext/intl/tests/calendar_get_debug_info_tz_leak.phpt new file mode 100644 index 000000000000..32b9da4368a9 --- /dev/null +++ b/ext/intl/tests/calendar_get_debug_info_tz_leak.phpt @@ -0,0 +1,26 @@ +--TEST-- +IntlCalendar get_debug_info() must not leak the time zone wrapper object +--EXTENSIONS-- +intl +--FILE-- + +--EXPECT-- +int(0) diff --git a/ext/intl/tests/collator_sort_with_sort_keys_buffer_size.phpt b/ext/intl/tests/collator_sort_with_sort_keys_buffer_size.phpt new file mode 100644 index 000000000000..ef1d68851e37 --- /dev/null +++ b/ext/intl/tests/collator_sort_with_sort_keys_buffer_size.phpt @@ -0,0 +1,57 @@ +--TEST-- +Collator::sortWithSortKeys() buffer allocation scales with array size +--EXTENSIONS-- +intl +--FILE-- +sort($a); + +$before = memory_get_peak_usage(); +$b = ['bb', 'aa', 'cc', 'ab', 'ca', 'bc', 'ac', 'ba']; +$c->sortWithSortKeys($b); +$peakDelta = memory_get_peak_usage() - $before; + +var_dump($a); +var_dump($b); +var_dump($peakDelta < 100000); + +$long = str_repeat('a', 10000); +$d = [$long . 'b', $long . 'a']; +$c->sortWithSortKeys($d); +echo $d[0] === $long . 'a' ? "long-a\n" : "fail-a\n"; +echo $d[1] === $long . 'b' ? "long-b\n" : "fail-b\n"; +?> +--EXPECT-- +array(4) { + [0]=> + string(2) "aa" + [1]=> + string(2) "bb" + [2]=> + string(2) "cc" + [3]=> + string(2) "dd" +} +array(8) { + [0]=> + string(2) "aa" + [1]=> + string(2) "ab" + [2]=> + string(2) "ac" + [3]=> + string(2) "ba" + [4]=> + string(2) "bb" + [5]=> + string(2) "bc" + [6]=> + string(2) "ca" + [7]=> + string(2) "cc" +} +bool(true) +long-a +long-b diff --git a/ext/mysqli/tests/fake_server.inc b/ext/mysqli/tests/fake_server.inc index dad8bc52ddd1..3af5c7459159 100644 --- a/ext/mysqli/tests/fake_server.inc +++ b/ext/mysqli/tests/fake_server.inc @@ -721,6 +721,19 @@ function my_mysqli_test_auth_response_message_over_read(my_mysqli_fake_server_co $conn->read(); } +function my_mysqli_test_ok_packet_message_over_read(my_mysqli_fake_server_conn $conn): void +{ + $p = new my_mysqli_fake_packet(); + $p->full = "08000001" . "00" . "00" . "00" . "0200" . "0000" . "fa"; + + $conn->send_server_greetings(); + $conn->read_packets(1); + $conn->send_server_ok(); + $conn->read_packets(1); + $conn->send($p->to_bytes(), "Malicious OK Packet [message length past the packet size]"); + $conn->read(); +} + function my_mysqli_test_stmt_response_row_over_read_string(my_mysqli_fake_server_conn $conn): void { $rh = $conn->packet_generator->server_stmt_execute_items_response(); @@ -816,6 +829,50 @@ function my_mysqli_test_stmt_response_row_read_two_fields(my_mysqli_fake_server_ } } +function my_mysqli_test_rset_field_metadata_len_over_read(my_mysqli_fake_server_conn $conn): void +{ + $rh = $conn->packet_generator->server_tabular_query_response(); + + $qr2 = new my_mysqli_fake_packet(); + $qr2->packet_length = "0c0000"; + $qr2->packet_number = "02"; + $qr2->catalog_length_plus_name = "0161"; + $qr2->db_length_plus_name = "0162"; + $qr2->table_length_plus_name = "0163"; + $qr2->original_t = "0164"; + $qr2->name_length_plus_name = "0165"; + $qr2->original_n = "fcff"; + + $conn->send_server_greetings(); + $conn->read_packets(1); + $conn->send_server_ok(); + $conn->read_packets(1); + $conn->send($conn->packets_to_bytes([$rh[0], $qr2]), "Malicious Tabular Response [metadata string length past the packet size]"); + $conn->read(); +} + +function my_mysqli_test_rset_field_metadata_len_past_packet(my_mysqli_fake_server_conn $conn): void +{ + $rh = $conn->packet_generator->server_tabular_query_response(); + + $qr2 = new my_mysqli_fake_packet(); + $qr2->packet_length = "0c0000"; + $qr2->packet_number = "02"; + $qr2->catalog_length_plus_name = "0161"; + $qr2->db_length_plus_name = "0162"; + $qr2->table_length_plus_name = "0163"; + $qr2->original_t = "0164"; + $qr2->name_length_plus_name = "0165"; + $qr2->original_n = "0561"; + + $conn->send_server_greetings(); + $conn->read_packets(1); + $conn->send_server_ok(); + $conn->read_packets(1); + $conn->send($conn->packets_to_bytes([$rh[0], $qr2]), "Malicious Tabular Response [metadata string length past the packet size]"); + $conn->read(); +} + function my_mysqli_test_query_response_row_length_overflow(my_mysqli_fake_server_conn $conn): void { $rh = $conn->packet_generator->server_query_execute_data_response('strval'); diff --git a/ext/mysqli/tests/mysqlnd_ok_packet_message_over_read.phpt b/ext/mysqli/tests/mysqlnd_ok_packet_message_over_read.phpt new file mode 100644 index 000000000000..8364251e8df4 --- /dev/null +++ b/ext/mysqli/tests/mysqlnd_ok_packet_message_over_read.phpt @@ -0,0 +1,40 @@ +--TEST-- +mysqlnd OK packet message length buffer over-read +--EXTENSIONS-- +mysqli +--FILE-- +wait(); + +try { + $conn = new mysqli( $servername, $username, $password, "", $process->getPort()); + var_dump($conn->select_db("test")); +} catch (Exception $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$process->terminate(); + +print "done!"; +?> +--EXPECTF-- +[*] Server started on 127.0.0.1:%d +[*] Connection established +[*] Sending - Server Greeting: %s +[*] Received: %s +[*] Sending - Server OK: %s +[*] Received: %s +[*] Sending - Malicious OK Packet [message length past the packet size]: %s + +Warning: mysqli::select_db(): OK packet message length is past the packet size in %s on line %d + +Warning: mysqli::select_db(): Error while reading INIT_DB's response packet. PID=%d in %s on line %d +mysqli_sql_exception: Malformed packet +done! diff --git a/ext/mysqli/tests/mysqlnd_rset_field_len_over_read.phpt b/ext/mysqli/tests/mysqlnd_rset_field_len_over_read.phpt new file mode 100644 index 000000000000..274468ded63d --- /dev/null +++ b/ext/mysqli/tests/mysqlnd_rset_field_len_over_read.phpt @@ -0,0 +1,42 @@ +--TEST-- +mysqlnd result set field metadata string length buffer over-read (len clamped to packet size) +--EXTENSIONS-- +mysqli +--FILE-- +wait(); + +try { + $conn = new mysqli( $servername, $username, $password, "", $process->getPort()); + var_dump($conn->query("SELECT * from users")); +} catch (Exception $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$conn->close(); + +$process->terminate(); + +print "done!"; +?> +--EXPECTF-- +[*] Server started on 127.0.0.1:%d +[*] Connection established +[*] Sending - Server Greeting: 580000000a352e352e352d31302e352e31382d4d6172696144420003000000473e3f6047257c6700fef7080200ff81150000000000000f0000006c6b55463f49335f686c6431006d7973716c5f6e61746976655f70617373776f7264 +[*] Received: %s +[*] Sending - Server OK: 0700000200000002000000 +[*] Received: %s +[*] Sending - Malicious Tabular Response [metadata string length past the packet size]: 01000001010c00000201610162016301640165fcff + +Warning: mysqli::query(): Premature end of data (mysqlnd_wireprotocol.c:%d) in %s on line %d + +Warning: mysqli::query(): Result set field packet %d bytes shorter than expected in %s on line %d +bool(false) +done! diff --git a/ext/mysqli/tests/mysqlnd_rset_field_len_past_packet.phpt b/ext/mysqli/tests/mysqlnd_rset_field_len_past_packet.phpt new file mode 100644 index 000000000000..020d28d95b25 --- /dev/null +++ b/ext/mysqli/tests/mysqlnd_rset_field_len_past_packet.phpt @@ -0,0 +1,40 @@ +--TEST-- +mysqlnd result set field metadata string length exceeds remaining packet bytes +--EXTENSIONS-- +mysqli +--FILE-- +wait(); + +try { + $conn = new mysqli( $servername, $username, $password, "", $process->getPort()); + var_dump($conn->query("SELECT * from users")); +} catch (Exception $e) { + echo $e::class, ": ", $e->getMessage(), PHP_EOL; +} + +$conn->close(); + +$process->terminate(); + +print "done!"; +?> +--EXPECTF-- +[*] Server started on 127.0.0.1:%d +[*] Connection established +[*] Sending - Server Greeting: %s +[*] Received: %s +[*] Sending - Server OK: %s +[*] Received: %s +[*] Sending - Malicious Tabular Response [metadata string length past the packet size]: %s + +Warning: mysqli::query(): Result set field metadata string length is past the packet size in %s on line %d +bool(false) +done! diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index b957240a4088..094daa939aa4 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -876,7 +876,12 @@ php_mysqlnd_ok_read(MYSQLND_CONN_DATA * conn, void * _packet) /* There is a message */ if (packet->header.size > (size_t) (p - buf) && (net_len = php_mysqlnd_net_field_length(&p))) { - packet->message_len = MIN(net_len, buf_len - (p - begin)); + if ((p - buf) > packet->header.size || packet->header.size - (p - buf) < net_len) { + DBG_ERR_FMT("OK packet message length is past the packet size"); + php_error_docref(NULL, E_WARNING, "OK packet message length is past the packet size"); + DBG_RETURN(FAIL); + } + packet->message_len = net_len; packet->message = mnd_pestrndup((char *)p, packet->message_len, FALSE); } else { packet->message = NULL; @@ -1169,10 +1174,17 @@ void php_mysqlnd_rset_header_free_mem(void * _packet) /* }}} */ #define READ_RSET_FIELD(field_name) do { \ + BAIL_IF_NO_MORE_DATA; \ len = php_mysqlnd_net_field_length(&p); \ if (UNEXPECTED(len == MYSQLND_NULL_LENGTH)) { \ goto faulty_or_fake; \ } else if (len != 0) { \ + BAIL_IF_NO_MORE_DATA; \ + if (UNEXPECTED((p - begin) > packet->header.size || packet->header.size - (p - begin) < len)) { \ + DBG_ERR_FMT("Result set field metadata string length is past the packet size"); \ + php_error_docref(NULL, E_WARNING, "Result set field metadata string length is past the packet size"); \ + DBG_RETURN(FAIL); \ + } \ meta->field_name = (const char *)p; \ meta->field_name ## _length = len; \ p += len; \ @@ -1241,7 +1253,7 @@ php_mysqlnd_rset_field_read(MYSQLND_CONN_DATA * conn, void * _packet) READ_RSET_FIELD(name); READ_RSET_FIELD(org_name); - /* 1 byte length */ + BAIL_IF_NO_MORE_DATA; if (UNEXPECTED(12 != *p)) { DBG_ERR_FMT("Protocol error. Server sent false length. Expected 12 got %d", (int) *p); php_error_docref(NULL, E_WARNING, "Protocol error. Server sent false length. Expected 12"); diff --git a/ext/simplexml/simplexml.c b/ext/simplexml/simplexml.c index 94c538a40488..74d310e9d938 100644 --- a/ext/simplexml/simplexml.c +++ b/ext/simplexml/simplexml.c @@ -436,8 +436,7 @@ static zval *sxe_prop_dim_write(zend_object *object, zval *member, zval *value, if (sxe->iter.type == SXE_ITER_ATTRLIST) { access_mode = SXE_ACCESS_ATTRIBS; - node = php_sxe_get_first_node_non_destructive(sxe, node); - attr = (xmlAttrPtr)node; + attr = (xmlAttrPtr)php_sxe_get_first_node_non_destructive(sxe, node); test = sxe->iter.name != NULL; } else if (sxe->iter.type != SXE_ITER_CHILD) { mynode = node; diff --git a/ext/simplexml/tests/attributes_dimension_write.phpt b/ext/simplexml/tests/attributes_dimension_write.phpt new file mode 100644 index 000000000000..8721dc7dc7c2 --- /dev/null +++ b/ext/simplexml/tests/attributes_dimension_write.phpt @@ -0,0 +1,30 @@ +--TEST-- +Creating new attributes via dimension and property writes on attributes() +--FILE-- +'); +$x->attributes()['new'] = 'v'; +echo $x->asXML(); + +$a = simplexml_load_string(''); +$a->attributes()['created'] = 'yes'; +echo $a->asXML(); + +$b = simplexml_load_string(''); +$attrs = $b->attributes(); +$attrs->other = 2; +echo $b->asXML(); + +$c = simplexml_load_string(''); +$c->attributes()['a'] = '2'; +echo $c->asXML(); +?> +--EXPECT-- + + + + + + + + diff --git a/ext/soap/php_sdl.c b/ext/soap/php_sdl.c index c6e53c408aa8..3062a2d4dbf6 100644 --- a/ext/soap/php_sdl.c +++ b/ext/soap/php_sdl.c @@ -1144,7 +1144,7 @@ static sdlPtr load_wsdl(zval *this_ptr, char *struri) return ctx.sdl; } -#define WSDL_CACHE_VERSION 0x10 +#define WSDL_CACHE_VERSION 0x11 #define WSDL_CACHE_GET(ret,type,buf) memcpy(&ret,*buf,sizeof(type)); *buf += sizeof(type); #define WSDL_CACHE_GET_INT(ret,buf) ret = ((unsigned char)(*buf)[0])|((unsigned char)(*buf)[1]<<8)|((unsigned char)(*buf)[2]<<16)|((unsigned)(*buf)[3]<<24); *buf += 4; @@ -2054,7 +2054,7 @@ static void sdl_serialize_soap_body(const sdlSoapBindingFunctionBodyPtr body, co sdlSoapBindingFunctionHeaderPtr tmp2; const zend_string *key_inner; - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(body->headers, key_inner, tmp2) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(tmp->headerfaults, key_inner, tmp2) { sdl_serialize_key(key_inner, out); WSDL_CACHE_PUT_1(tmp2->use, out); if (tmp2->use == SOAP_ENCODED) { diff --git a/ext/soap/soap.c b/ext/soap/soap.c index 402708bb3095..506a46113861 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -1583,7 +1583,11 @@ PHP_METHOD(SoapServer, handle) /* If new session or something weird happned */ if (soap_obj == NULL) { - object_init_ex(&tmp_soap, service->soap_class.ce); + if (UNEXPECTED(object_init_ex(&tmp_soap, service->soap_class.ce) != SUCCESS)) { + php_output_discard(); + _soap_server_exception(service, function, ZEND_THIS); + goto fail; + } /* Call constructor */ if (service->soap_class.ce->constructor) { diff --git a/ext/soap/tests/gh23447.phpt b/ext/soap/tests/gh23447.phpt new file mode 100644 index 000000000000..16ecc4568ebd --- /dev/null +++ b/ext/soap/tests/gh23447.phpt @@ -0,0 +1,28 @@ +--TEST-- +GH-23447 (Segfault when a class passed to SoapServer::setClass() fails to initialize) +--EXTENSIONS-- +soap +--CREDITS-- +Lu Maltsis (@lmaltsis) +--FILE-- + 'http://testuri.org']); +$server->setClass('foo'); + +$server->handle(<<<'XML' + + + + +XML); + +echo "ok\n"; +?> +--EXPECT-- + +SOAP-ENV:ServerUndefined constant "undefinedConstant" +ok diff --git a/ext/soap/tests/headerfault_cache.phpt b/ext/soap/tests/headerfault_cache.phpt new file mode 100644 index 000000000000..6da747902d80 --- /dev/null +++ b/ext/soap/tests/headerfault_cache.phpt @@ -0,0 +1,45 @@ +--TEST-- +WSDL cache corruption when soap:header has headerfaults +--EXTENSIONS-- +soap +--INI-- +soap.wsdl_cache_enabled=1 +--FILE-- + WSDL_CACHE_DISK]; + +$c1 = new SoapClient(__DIR__ . '/headerfault_cache.wsdl', $options); +var_dump($c1->__getFunctions()); + +$c2 = new SoapClient(__DIR__ . '/headerfault_cache.wsdl', $options); +var_dump($c2->__getFunctions()); + +echo "ok\n"; +?> +--CLEAN-- + +--EXPECT-- +array(1) { + [0]=> + string(32) "string testHeader(string $param)" +} +array(1) { + [0]=> + string(32) "string testHeader(string $param)" +} +ok diff --git a/ext/soap/tests/headerfault_cache.wsdl b/ext/soap/tests/headerfault_cache.wsdl new file mode 100644 index 000000000000..8a844c0b899d --- /dev/null +++ b/ext/soap/tests/headerfault_cache.wsdl @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ext/sockets/sockets.c b/ext/sockets/sockets.c index c4c6f224dcfa..66df65657988 100644 --- a/ext/sockets/sockets.c +++ b/ext/sockets/sockets.c @@ -2341,13 +2341,26 @@ PHP_FUNCTION(socket_set_option) #ifdef SO_ATTACH_REUSEPORT_CBPF case SO_ATTACH_REUSEPORT_CBPF: { + if (level != SOL_SOCKET) { + php_error_docref(NULL, E_WARNING, "Invalid level"); + RETURN_FALSE; + } + if (Z_TYPE_P(arg4) != IS_LONG) { + zend_argument_type_error(4, "must be of type int when argument #3 ($option) is SO_ATTACH_REUSEPORT_CBPF, %s given", zend_zval_value_name(arg4)); + RETURN_THROWS(); + } zend_long cbpf_val = zval_get_long(arg4); if (!cbpf_val) { +#ifdef SO_DETACH_REUSEPORT_BPF ov = 1; optlen = sizeof(ov); opt_ptr = &ov; - optname = SO_DETACH_BPF; + optname = SO_DETACH_REUSEPORT_BPF; +#else + php_error_docref(NULL, E_WARNING, "Detaching a reuseport CBPF filter is unsupported"); + RETURN_FALSE; +#endif } else { uint32_t k = (uint32_t)cbpf_val; diff --git a/ext/sockets/sockets.stub.php b/ext/sockets/sockets.stub.php index fab32628544d..681d5ba6d0ff 100644 --- a/ext/sockets/sockets.stub.php +++ b/ext/sockets/sockets.stub.php @@ -1898,6 +1898,13 @@ */ const SO_ATTACH_REUSEPORT_CBPF = UNKNOWN; #endif +#if defined(SO_DETACH_REUSEPORT_BPF) +/** + * @var int + * @cvalue SO_DETACH_REUSEPORT_BPF + */ +const SO_DETACH_REUSEPORT_BPF = UNKNOWN; +#endif #if defined(SO_DETACH_FILTER) /** * @var int diff --git a/ext/sockets/sockets_arginfo.h b/ext/sockets/sockets_arginfo.h index cfd792244084..203c010f5171 100644 --- a/ext/sockets/sockets_arginfo.h +++ b/ext/sockets/sockets_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit sockets.stub.php instead. - * Stub hash: 711d3b84051445917c4a8a1d0cdc1d0c6328be07 */ + * Stub hash: aceee39bed5332f7f26d5d768976c4d5ab96ab3c */ 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) @@ -990,6 +990,9 @@ static void register_sockets_symbols(int module_number) #if defined(SO_ATTACH_REUSEPORT_CBPF) REGISTER_LONG_CONSTANT("SO_ATTACH_REUSEPORT_CBPF", SO_ATTACH_REUSEPORT_CBPF, CONST_PERSISTENT); #endif +#if defined(SO_DETACH_REUSEPORT_BPF) + REGISTER_LONG_CONSTANT("SO_DETACH_REUSEPORT_BPF", SO_DETACH_REUSEPORT_BPF, CONST_PERSISTENT); +#endif #if defined(SO_DETACH_FILTER) REGISTER_LONG_CONSTANT("SO_DETACH_FILTER", SO_DETACH_FILTER, CONST_PERSISTENT); #endif diff --git a/ext/sockets/tests/socket_reuseport_cbpf.phpt b/ext/sockets/tests/socket_reuseport_cbpf.phpt index 2210c4438f00..a9b8d5c731ff 100644 --- a/ext/sockets/tests/socket_reuseport_cbpf.phpt +++ b/ext/sockets/tests/socket_reuseport_cbpf.phpt @@ -19,19 +19,18 @@ if (!$socket) { var_dump(socket_set_option( $socket, SOL_SOCKET, SO_REUSEADDR, true)); var_dump(socket_set_option( $socket, SOL_SOCKET, SO_REUSEPORT, true)); try { - socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, array()); + socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, []); } catch (\TypeError $e) { - echo $e->getMessage() . PHP_EOL; + echo $e::class, ': ', $e->getMessage(), "\n"; } var_dump(socket_set_option( $socket, SOL_SOCKET, SO_ATTACH_REUSEPORT_CBPF, SKF_AD_CPU)); var_dump(socket_bind($socket, '0.0.0.0')); socket_listen($socket); socket_close($socket); ?> ---EXPECTF-- +--EXPECT-- bool(true) bool(true) - -Warning: socket_set_option(): Unable to set socket option [2]: No such file or directory in %s on line %d +TypeError: socket_set_option(): Argument #4 ($value) must be of type int when argument #3 ($option) is SO_ATTACH_REUSEPORT_CBPF, array given bool(true) bool(true) diff --git a/ext/sockets/tests/socket_reuseport_cbpf_detach.phpt b/ext/sockets/tests/socket_reuseport_cbpf_detach.phpt new file mode 100644 index 000000000000..bd1608a132d5 --- /dev/null +++ b/ext/sockets/tests/socket_reuseport_cbpf_detach.phpt @@ -0,0 +1,38 @@ +--TEST-- +socket_set_option() attach/detach round trip for reuseport CBPF filters +--EXTENSIONS-- +sockets +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) + +Warning: socket_set_option(): Unable to set socket option [%d]: %s in %s on line %d +bool(false) +bool(true) +bool(true) diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 3e23934cbc78..8ae94d8c6d99 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -2451,7 +2451,10 @@ function str_contains(string $haystack, string $needle): bool {} */ function str_starts_with(string $haystack, string $needle): bool {} -/** @compile-time-eval */ +/** + * @compile-time-eval + * @frameless-function {"arity": 2} + */ function str_ends_with(string $haystack, string $needle): bool {} /** diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 442085e9d6cc..4bcf008f5fdd 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: c645e310c00d9f4cb3856c94ee60d06071e28de0 + * Stub hash: 31018a787ba261316941b0d88f090b9cf271aa0e * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) @@ -2294,6 +2294,12 @@ static const zend_frameless_function_info frameless_function_infos_str_starts_wi { 0 }, }; +ZEND_FRAMELESS_FUNCTION(str_ends_with, 2); +static const zend_frameless_function_info frameless_function_infos_str_ends_with[] = { + { ZEND_FRAMELESS_FUNCTION_NAME(str_ends_with, 2), 2 }, + { 0 }, +}; + ZEND_FRAMELESS_FUNCTION(substr, 2); ZEND_FRAMELESS_FUNCTION(substr, 3); static const zend_frameless_function_info frameless_function_infos_substr[] = { @@ -3197,7 +3203,7 @@ static const zend_function_entry ext_functions[] = { ZEND_RAW_FENTRY("strrchr", zif_strrchr, arginfo_strrchr, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("str_contains", zif_str_contains, arginfo_str_contains, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_str_contains, NULL) ZEND_RAW_FENTRY("str_starts_with", zif_str_starts_with, arginfo_str_starts_with, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_str_starts_with, NULL) - ZEND_RAW_FENTRY("str_ends_with", zif_str_ends_with, arginfo_str_ends_with, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) + ZEND_RAW_FENTRY("str_ends_with", zif_str_ends_with, arginfo_str_ends_with, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_str_ends_with, NULL) ZEND_RAW_FENTRY("chunk_split", zif_chunk_split, arginfo_chunk_split, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("substr", zif_substr, arginfo_substr, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_substr, NULL) ZEND_RAW_FENTRY("substr_replace", zif_substr_replace, arginfo_substr_replace, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) diff --git a/ext/standard/basic_functions_decl.h b/ext/standard/basic_functions_decl.h index f2f234f60cc2..db81e0bfc077 100644 --- a/ext/standard/basic_functions_decl.h +++ b/ext/standard/basic_functions_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit basic_functions.stub.php instead. - * Stub hash: c645e310c00d9f4cb3856c94ee60d06071e28de0 */ + * Stub hash: 31018a787ba261316941b0d88f090b9cf271aa0e */ -#ifndef ZEND_BASIC_FUNCTIONS_DECL_c645e310c00d9f4cb3856c94ee60d06071e28de0_H -#define ZEND_BASIC_FUNCTIONS_DECL_c645e310c00d9f4cb3856c94ee60d06071e28de0_H +#ifndef ZEND_BASIC_FUNCTIONS_DECL_31018a787ba261316941b0d88f090b9cf271aa0e_H +#define ZEND_BASIC_FUNCTIONS_DECL_31018a787ba261316941b0d88f090b9cf271aa0e_H typedef enum zend_enum_SortDirection { ZEND_ENUM_SortDirection_Ascending = 1, @@ -20,4 +20,4 @@ typedef enum zend_enum_RoundingMode { ZEND_ENUM_RoundingMode_PositiveInfinity = 8, } zend_enum_RoundingMode; -#endif /* ZEND_BASIC_FUNCTIONS_DECL_c645e310c00d9f4cb3856c94ee60d06071e28de0_H */ +#endif /* ZEND_BASIC_FUNCTIONS_DECL_31018a787ba261316941b0d88f090b9cf271aa0e_H */ diff --git a/ext/standard/string.c b/ext/standard/string.c index e5307a4f2d4b..af3f6a461dcf 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -1895,6 +1895,21 @@ PHP_FUNCTION(str_ends_with) } /* }}} */ +ZEND_FRAMELESS_FUNCTION(str_ends_with, 2) +{ + zval haystack_tmp, needle_tmp; + zend_string *haystack, *needle; + + Z_FLF_PARAM_STR(1, haystack, haystack_tmp); + Z_FLF_PARAM_STR(2, needle, needle_tmp); + + RETVAL_BOOL(zend_string_ends_with(haystack, needle)); + +flf_clean: + Z_FLF_PARAM_FREE_STR(1, haystack_tmp); + Z_FLF_PARAM_FREE_STR(2, needle_tmp); +} + static zend_always_inline void _zend_strpos(zval *return_value, zend_string *haystack, zend_string *needle, zend_long offset) { const char *found = NULL; diff --git a/ext/uri/php_uri.c b/ext/uri/php_uri.c index 4caeca9f1465..bb1d8c8bb13c 100644 --- a/ext/uri/php_uri.c +++ b/ext/uri/php_uri.c @@ -36,6 +36,7 @@ zend_class_entry *php_uri_ce_rfc3986_uri_type; zend_class_entry *php_uri_ce_rfc3986_uri_host_type; zend_class_entry *php_uri_ce_whatwg_url_builder; zend_class_entry *php_uri_ce_whatwg_url; +zend_class_entry *php_uri_ce_whatwg_url_percent_encoding_mode; zend_class_entry *php_uri_ce_comparison_mode; zend_class_entry *php_uri_ce_exception; zend_class_entry *php_uri_ce_error; @@ -1073,6 +1074,60 @@ PHP_METHOD(Uri_WhatWg_Url, __debugInfo) RETURN_ARR(uri_get_debug_properties(uri_object)); } +PHP_FUNCTION(Uri_WhatWg_url_percent_encode) +{ + zend_string *input; + zend_enum_Uri_WhatWg_UrlPercentEncodingMode mode; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(input) + Z_PARAM_ENUM(mode, php_uri_ce_whatwg_url_percent_encoding_mode) + ZEND_PARSE_PARAMETERS_END(); + + zend_string *str; + + switch (mode) { + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Username: + ZEND_FALLTHROUGH; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Password: + str = php_uri_parser_whatwg_percent_encode_userinfo_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_OpaqueHost: + str = php_uri_parser_whatwg_percent_encode_opaque_host_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Path: + str = php_uri_parser_whatwg_percent_encode_path_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_OpaquePath: + str = php_uri_parser_whatwg_percent_encode_opaque_path_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_PathSegment: + str = php_uri_parser_whatwg_percent_encode_path_segment_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Query: + str = php_uri_parser_whatwg_percent_encode_query_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_SpecialQuery: + str = php_uri_parser_whatwg_percent_encode_special_query_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_FormQuery: + str = php_uri_parser_whatwg_percent_encode_form_query_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + case ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Fragment: + str = php_uri_parser_whatwg_percent_encode_fragment_component(ZSTR_VAL(input), ZSTR_LEN(input)); + break; + default: ZEND_UNREACHABLE(); + } + + /* This should be unreachable in practice, as str is null only due to memory errors. */ + if (str == NULL) { + zend_throw_exception(php_uri_ce_error, "Cannot percent-encode input", 0); + RETURN_THROWS(); + } + + RETURN_NEW_STR(str); +} + PHP_METHOD(Uri_Rfc3986_UriBuilder, reset) { ZEND_PARSE_PARAMETERS_NONE(); @@ -1481,6 +1536,8 @@ static PHP_MINIT_FUNCTION(uri) object_handlers_whatwg_uri.free_obj = php_uri_object_handler_free; object_handlers_whatwg_uri.clone_obj = php_uri_object_handler_clone; + php_uri_ce_whatwg_url_percent_encoding_mode = register_class_Uri_WhatWg_UrlPercentEncodingMode(); + php_uri_ce_comparison_mode = register_class_Uri_UriComparisonMode(); php_uri_ce_exception = register_class_Uri_UriException(zend_ce_exception); php_uri_ce_error = register_class_Uri_UriError(zend_ce_error); @@ -1548,14 +1605,14 @@ ZEND_MODULE_POST_ZEND_DEACTIVATE_D(uri) zend_module_entry uri_module_entry = { STANDARD_MODULE_HEADER_EX, NULL, uri_deps, - "uri", /* Extension name */ - NULL, /* zend_function_entry */ + "uri", /* Extension name */ + ext_functions, /* zend_function_entry */ PHP_MINIT(uri), /* PHP_MINIT - Module initialization */ - PHP_MSHUTDOWN(uri), /* PHP_MSHUTDOWN - Module shutdown */ + PHP_MSHUTDOWN(uri), /* PHP_MSHUTDOWN - Module shutdown */ PHP_RINIT(uri), /* PHP_RINIT - Request initialization */ - NULL, /* PHP_RSHUTDOWN - Request shutdown */ - PHP_MINFO(uri), /* PHP_MINFO - Module info */ - PHP_VERSION, /* Version */ + NULL, /* PHP_RSHUTDOWN - Request shutdown */ + PHP_MINFO(uri), /* PHP_MINFO - Module info */ + PHP_VERSION, /* Version */ NO_MODULE_GLOBALS, ZEND_MODULE_POST_ZEND_DEACTIVATE_N(uri), STANDARD_MODULE_PROPERTIES_EX diff --git a/ext/uri/php_uri.stub.php b/ext/uri/php_uri.stub.php index 98f8873d9845..ad1d2fe32dee 100644 --- a/ext/uri/php_uri.stub.php +++ b/ext/uri/php_uri.stub.php @@ -317,4 +317,20 @@ public function __unserialize(array $data): void {} public function __debugInfo(): array {} } + + enum UrlPercentEncodingMode + { + case Username; + case Password; + case OpaqueHost; + case Path; + case OpaquePath; + case PathSegment; + case Query; + case SpecialQuery; + case FormQuery; + case Fragment; + } + + function url_percent_encode(string $input, \Uri\WhatWg\UrlPercentEncodingMode $mode): string {} } diff --git a/ext/uri/php_uri_arginfo.h b/ext/uri/php_uri_arginfo.h index 93cc3ee45a2b..c77d0485d052 100644 --- a/ext/uri/php_uri_arginfo.h +++ b/ext/uri/php_uri_arginfo.h @@ -1,7 +1,12 @@ /* This is a generated file, edit php_uri.stub.php instead. - * Stub hash: 54e953b1da0d08c64509666b9278c59483d1e171 + * Stub hash: 9e087e3aefdab5662892e7fad9de87857aa63057 * Has decl header: yes */ +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_Uri_WhatWg_url_percent_encode, 0, 2, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, input, IS_STRING, 0) + ZEND_ARG_OBJ_INFO(0, mode, Uri\\WhatWg\\\125rlPercentEncodingMode, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Uri_Rfc3986_UriBuilder_reset, 0, 0, IS_STATIC, 0) ZEND_END_ARG_INFO() @@ -242,6 +247,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_Uri_WhatWg_Url___debugInfo arginfo_class_Uri_Rfc3986_Uri___serialize +ZEND_FUNCTION(Uri_WhatWg_url_percent_encode); ZEND_METHOD(Uri_Rfc3986_UriBuilder, reset); ZEND_METHOD(Uri_Rfc3986_UriBuilder, setScheme); ZEND_METHOD(Uri_Rfc3986_UriBuilder, setUserInfo); @@ -316,6 +322,11 @@ ZEND_METHOD(Uri_WhatWg_Url, __serialize); ZEND_METHOD(Uri_WhatWg_Url, __unserialize); ZEND_METHOD(Uri_WhatWg_Url, __debugInfo); +static const zend_function_entry ext_functions[] = { + ZEND_RAW_FENTRY(ZEND_NS_NAME("Uri\\WhatWg", "url_percent_encode"), zif_Uri_WhatWg_url_percent_encode, arginfo_Uri_WhatWg_url_percent_encode, 0, NULL, NULL) + ZEND_FE_END +}; + static const zend_function_entry class_Uri_Rfc3986_UriBuilder_methods[] = { ZEND_ME(Uri_Rfc3986_UriBuilder, reset, arginfo_class_Uri_Rfc3986_UriBuilder_reset, ZEND_ACC_PUBLIC) ZEND_ME(Uri_Rfc3986_UriBuilder, setScheme, arginfo_class_Uri_Rfc3986_UriBuilder_setScheme, ZEND_ACC_PUBLIC) @@ -733,3 +744,30 @@ static zend_class_entry *register_class_Uri_WhatWg_Url(void) return class_entry; } + +static zend_class_entry *register_class_Uri_WhatWg_UrlPercentEncodingMode(void) +{ + zend_class_entry *class_entry = zend_register_internal_enum("Uri\\WhatWg\\UrlPercentEncodingMode", IS_UNDEF, NULL); + + zend_enum_add_case_cstr(class_entry, "Username", NULL); + + zend_enum_add_case_cstr(class_entry, "Password", NULL); + + zend_enum_add_case_cstr(class_entry, "OpaqueHost", NULL); + + zend_enum_add_case_cstr(class_entry, "Path", NULL); + + zend_enum_add_case_cstr(class_entry, "OpaquePath", NULL); + + zend_enum_add_case_cstr(class_entry, "PathSegment", NULL); + + zend_enum_add_case_cstr(class_entry, "Query", NULL); + + zend_enum_add_case_cstr(class_entry, "SpecialQuery", NULL); + + zend_enum_add_case_cstr(class_entry, "FormQuery", NULL); + + zend_enum_add_case_cstr(class_entry, "Fragment", NULL); + + return class_entry; +} diff --git a/ext/uri/php_uri_common.h b/ext/uri/php_uri_common.h index 31ef1dd2130c..a1d9d852f3b7 100644 --- a/ext/uri/php_uri_common.h +++ b/ext/uri/php_uri_common.h @@ -23,6 +23,7 @@ extern zend_class_entry *php_uri_ce_rfc3986_uri_type; extern zend_class_entry *php_uri_ce_rfc3986_uri_host_type; extern zend_class_entry *php_uri_ce_whatwg_url_builder; extern zend_class_entry *php_uri_ce_whatwg_url; +extern zend_class_entry *php_uri_ce_whatwg_url_percent_encoding_mode; extern zend_class_entry *php_uri_ce_comparison_mode; extern zend_class_entry *php_uri_ce_exception; extern zend_class_entry *php_uri_ce_error; diff --git a/ext/uri/php_uri_decl.h b/ext/uri/php_uri_decl.h index 71f748b71070..a55b44a95205 100644 --- a/ext/uri/php_uri_decl.h +++ b/ext/uri/php_uri_decl.h @@ -1,8 +1,8 @@ /* This is a generated file, edit php_uri.stub.php instead. - * Stub hash: 54e953b1da0d08c64509666b9278c59483d1e171 */ + * Stub hash: 9e087e3aefdab5662892e7fad9de87857aa63057 */ -#ifndef ZEND_PHP_URI_DECL_54e953b1da0d08c64509666b9278c59483d1e171_H -#define ZEND_PHP_URI_DECL_54e953b1da0d08c64509666b9278c59483d1e171_H +#ifndef ZEND_PHP_URI_DECL_9e087e3aefdab5662892e7fad9de87857aa63057_H +#define ZEND_PHP_URI_DECL_9e087e3aefdab5662892e7fad9de87857aa63057_H typedef enum zend_enum_Uri_UriComparisonMode { ZEND_ENUM_Uri_UriComparisonMode_IncludeFragment = 1, @@ -63,4 +63,17 @@ typedef enum zend_enum_Uri_WhatWg_UrlHostType { ZEND_ENUM_Uri_WhatWg_UrlHostType_Empty = 5, } zend_enum_Uri_WhatWg_UrlHostType; -#endif /* ZEND_PHP_URI_DECL_54e953b1da0d08c64509666b9278c59483d1e171_H */ +typedef enum zend_enum_Uri_WhatWg_UrlPercentEncodingMode { + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Username = 1, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Password = 2, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_OpaqueHost = 3, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Path = 4, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_OpaquePath = 5, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_PathSegment = 6, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Query = 7, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_SpecialQuery = 8, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_FormQuery = 9, + ZEND_ENUM_Uri_WhatWg_UrlPercentEncodingMode_Fragment = 10, +} zend_enum_Uri_WhatWg_UrlPercentEncodingMode; + +#endif /* ZEND_PHP_URI_DECL_9e087e3aefdab5662892e7fad9de87857aa63057_H */ diff --git a/ext/uri/tests/whatwg/percent_encoding/form_query_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/form_query_success_control.phpt new file mode 100644 index 000000000000..a5dd0ce69038 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/form_query_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - form query - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/form_query_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/form_query_success_percent.phpt new file mode 100644 index 000000000000..238f1b0b01f7 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/form_query_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - form query - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/form_query_success_space.phpt b/ext/uri/tests/whatwg/percent_encoding/form_query_success_space.phpt new file mode 100644 index 000000000000..50d649454213 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/form_query_success_space.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - form query - space code point +--FILE-- + +--EXPECT-- +string(10) "WHATWG+url" diff --git a/ext/uri/tests/whatwg/percent_encoding/form_query_success_special_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/form_query_success_special_in_set.phpt new file mode 100644 index 000000000000..82f622575917 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/form_query_success_special_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - form query - special code points in the percent-encode set +--FILE-- +&+,', Uri\WhatWg\UrlPercentEncodingMode::FormQuery)); + +?> +--EXPECT-- +string(21) "%22%23%3C%3E%26%2B%2C" diff --git a/ext/uri/tests/whatwg/percent_encoding/form_query_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/form_query_success_special_not_in_set.phpt new file mode 100644 index 000000000000..590f6dd31023 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/form_query_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - form query - special code points not in the percent-encode set +--FILE-- + +--EXPECT-- +string(4) "*-._" diff --git a/ext/uri/tests/whatwg/percent_encoding/form_query_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/form_query_success_unicode.phpt new file mode 100644 index 000000000000..bcb843ca67a6 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/form_query_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - form query - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/fragment_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/fragment_success_control.phpt new file mode 100644 index 000000000000..c0992a9e260f --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/fragment_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - fragment - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/fragment_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/fragment_success_percent.phpt new file mode 100644 index 000000000000..12b2bada8fae --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/fragment_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - fragment - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/fragment_success_special_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/fragment_success_special_in_set.phpt new file mode 100644 index 000000000000..fe129d66895f --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/fragment_success_special_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - fragment - special code points in the percent-encode set +--FILE-- +`', Uri\WhatWg\UrlPercentEncodingMode::Fragment)); + +?> +--EXPECT-- +string(15) "%20%22%3C%3E%60" diff --git a/ext/uri/tests/whatwg/percent_encoding/fragment_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/fragment_success_special_not_in_set.phpt new file mode 100644 index 000000000000..255c5173ef83 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/fragment_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - fragment - special code points not in the percent-encode set +--FILE-- + +--EXPECT-- +string(5) "('$+)" diff --git a/ext/uri/tests/whatwg/percent_encoding/fragment_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/fragment_success_unicode.phpt new file mode 100644 index 000000000000..0a14a113f386 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/fragment_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - fragment - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_control.phpt new file mode 100644 index 000000000000..7adc5b963787 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - opaque host - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_percent.phpt new file mode 100644 index 000000000000..f6c6d12e4c7b --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - opaque host - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_special_not_in_set.phpt new file mode 100644 index 000000000000..a170079625f9 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - opaque host - special code points not in the percent-encode set +--FILE-- +?^`{}@'$+,", Uri\WhatWg\UrlPercentEncodingMode::OpaqueHost)); + +?> +--EXPECT-- +string(15) " "#<>?^`{}@'$+," diff --git a/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_unicode.phpt new file mode 100644 index 000000000000..b51ad97b54dc --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/opaque_host_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - opaque host - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_control.phpt new file mode 100644 index 000000000000..adf1bee19b6c --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - opaque path - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_percent.phpt new file mode 100644 index 000000000000..7fa6a7c3973f --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - opaque path - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_special_not_in_set.phpt new file mode 100644 index 000000000000..fa9075856a5c --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - opaque path - special code points not in the percent-encode set +--FILE-- +?^`{}@'$+,", Uri\WhatWg\UrlPercentEncodingMode::OpaquePath)); + +?> +--EXPECT-- +string(15) " "#<>?^`{}@'$+," diff --git a/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_unicode.phpt new file mode 100644 index 000000000000..3fbae7c2c156 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/opaque_path_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - opaque path - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/password_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/password_success_control.phpt new file mode 100644 index 000000000000..57ff019878df --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/password_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - password - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/password_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/password_success_percent.phpt new file mode 100644 index 000000000000..536a2702a428 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/password_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - password - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/password_success_special_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/password_success_special_in_set.phpt new file mode 100644 index 000000000000..f4041e059e88 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/password_success_special_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - password - special code points in the percent-encode set +--FILE-- + +--EXPECT-- +string(21) "%5B%23%3D%3F%20%60%5D" diff --git a/ext/uri/tests/whatwg/percent_encoding/password_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/password_success_special_not_in_set.phpt new file mode 100644 index 000000000000..a4a151c44487 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/password_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - password - special code points not in the percent-encode set +--FILE-- + +--EXPECT-- +string(5) "('$+)" diff --git a/ext/uri/tests/whatwg/percent_encoding/password_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/password_success_unicode.phpt new file mode 100644 index 000000000000..d2d0247f98f3 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/password_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - password - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/path_segment_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_control.phpt new file mode 100644 index 000000000000..0c9bfcbb0b1e --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path segment - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/path_segment_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_percent.phpt new file mode 100644 index 000000000000..258cca36a2ee --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path segment - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/path_segment_success_special_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_special_in_set.phpt new file mode 100644 index 000000000000..03f43a8a0405 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_special_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path segment - special code points in the percent-encode set +--FILE-- +?^`{}/', Uri\WhatWg\UrlPercentEncodingMode::PathSegment)); + +?> +--EXPECT-- +string(33) "%20%22%23%3C%3E%3F%5E%60%7B%7D%2F" diff --git a/ext/uri/tests/whatwg/percent_encoding/path_segment_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_special_not_in_set.phpt new file mode 100644 index 000000000000..50790f66eb6c --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path segment - special code points not in the percent-encode set +--FILE-- + +--EXPECT-- +string(5) "@'$+," diff --git a/ext/uri/tests/whatwg/percent_encoding/path_segment_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_unicode.phpt new file mode 100644 index 000000000000..218d120ae9ea --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_segment_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path segment - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/path_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/path_success_control.phpt new file mode 100644 index 000000000000..fa56acc3a3c5 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/path_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/path_success_percent.phpt new file mode 100644 index 000000000000..013c9e770740 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/path_success_special_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/path_success_special_in_set.phpt new file mode 100644 index 000000000000..785c82902620 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_success_special_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path - special code points in the percent-encode set +--FILE-- +?^`{}', Uri\WhatWg\UrlPercentEncodingMode::Path)); + +?> +--EXPECT-- +string(30) "%20%22%23%3C%3E%3F%5E%60%7B%7D" diff --git a/ext/uri/tests/whatwg/percent_encoding/path_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/path_success_special_not_in_set.phpt new file mode 100644 index 000000000000..17f6bf915407 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path - special code points not in the percent-encode set +--FILE-- + +--EXPECT-- +string(5) "@'$+," diff --git a/ext/uri/tests/whatwg/percent_encoding/path_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/path_success_unicode.phpt new file mode 100644 index 000000000000..edfa72359251 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/path_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - path - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/query_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/query_success_control.phpt new file mode 100644 index 000000000000..2502cc7a662c --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/query_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - query - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/query_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/query_success_percent.phpt new file mode 100644 index 000000000000..e9a4187b963d --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/query_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - query - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/query_success_space.phpt b/ext/uri/tests/whatwg/percent_encoding/query_success_space.phpt new file mode 100644 index 000000000000..b9a491018580 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/query_success_space.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - query - space code point +--FILE-- + +--EXPECT-- +string(12) "WHATWG%20url" diff --git a/ext/uri/tests/whatwg/percent_encoding/query_success_special_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/query_success_special_in_set.phpt new file mode 100644 index 000000000000..a5fab6b7cd08 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/query_success_special_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - query - special code points in the percent-encode set +--FILE-- +', Uri\WhatWg\UrlPercentEncodingMode::Query)); + +?> +--EXPECT-- +string(15) "%20%22%23%3C%3E" diff --git a/ext/uri/tests/whatwg/percent_encoding/query_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/query_success_special_not_in_set.phpt new file mode 100644 index 000000000000..c8e4123d93ff --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/query_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - query - special code points not in the percent-encode set +--FILE-- + +--EXPECT-- +string(6) "[@?&]'" diff --git a/ext/uri/tests/whatwg/percent_encoding/query_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/query_success_unicode.phpt new file mode 100644 index 000000000000..f5b3988f08fe --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/query_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - query - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/special_query_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/special_query_success_control.phpt new file mode 100644 index 000000000000..f6d76153043f --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/special_query_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - special query - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/special_query_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/special_query_success_percent.phpt new file mode 100644 index 000000000000..a1683a1ef77b --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/special_query_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - special query - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/special_query_success_space.phpt b/ext/uri/tests/whatwg/percent_encoding/special_query_success_space.phpt new file mode 100644 index 000000000000..049a5da1d4ed --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/special_query_success_space.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - special query - space code point +--FILE-- + +--EXPECT-- +string(12) "WHATWG%20url" diff --git a/ext/uri/tests/whatwg/percent_encoding/special_query_success_special_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/special_query_success_special_in_set.phpt new file mode 100644 index 000000000000..e84df8465c9f --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/special_query_success_special_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - special query - special code points in the percent-encode set +--FILE-- +", Uri\WhatWg\UrlPercentEncodingMode::SpecialQuery)); + +?> +--EXPECT-- +string(18) "%20%27%22%23%3C%3E" diff --git a/ext/uri/tests/whatwg/percent_encoding/special_query_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/special_query_success_special_not_in_set.phpt new file mode 100644 index 000000000000..0ee677b31fe1 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/special_query_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - special query - special code points not in the percent-encode set +--FILE-- + +--EXPECT-- +string(5) "[@?&]" diff --git a/ext/uri/tests/whatwg/percent_encoding/special_query_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/special_query_success_unicode.phpt new file mode 100644 index 000000000000..b583d8de2ecc --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/special_query_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - special query - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/tests/whatwg/percent_encoding/username_success_control.phpt b/ext/uri/tests/whatwg/percent_encoding/username_success_control.phpt new file mode 100644 index 000000000000..5a64393065c9 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/username_success_control.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - username - control code points +--FILE-- + +--EXPECT-- +string(3) "%11" diff --git a/ext/uri/tests/whatwg/percent_encoding/username_success_percent.phpt b/ext/uri/tests/whatwg/percent_encoding/username_success_percent.phpt new file mode 100644 index 000000000000..9ea2831efa46 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/username_success_percent.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - username - percent sign code point +--FILE-- + +--EXPECT-- +string(14) "WHATWG%2520url" diff --git a/ext/uri/tests/whatwg/percent_encoding/username_success_special_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/username_success_special_in_set.phpt new file mode 100644 index 000000000000..070250ef3630 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/username_success_special_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - username - special code points in the percent-encode set +--FILE-- + +--EXPECT-- +string(21) "%5B%23%3D%3F%20%60%5D" diff --git a/ext/uri/tests/whatwg/percent_encoding/username_success_special_not_in_set.phpt b/ext/uri/tests/whatwg/percent_encoding/username_success_special_not_in_set.phpt new file mode 100644 index 000000000000..8626b96ab9b3 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/username_success_special_not_in_set.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - username - special code points not in the percent-encode set +--FILE-- + +--EXPECT-- +string(5) "('$+)" diff --git a/ext/uri/tests/whatwg/percent_encoding/username_success_unicode.phpt b/ext/uri/tests/whatwg/percent_encoding/username_success_unicode.phpt new file mode 100644 index 000000000000..d85b0712c1f0 --- /dev/null +++ b/ext/uri/tests/whatwg/percent_encoding/username_success_unicode.phpt @@ -0,0 +1,10 @@ +--TEST-- +Test Uri\WhatWg\url_percent_encode() - username - Unicode code points +--FILE-- + +--EXPECT-- +string(13) "f%C3%B6%C5%91" diff --git a/ext/uri/uri_parser_whatwg.c b/ext/uri/uri_parser_whatwg.c index b7a74fc1e231..4f308ba224bd 100644 --- a/ext/uri/uri_parser_whatwg.c +++ b/ext/uri/uri_parser_whatwg.c @@ -25,6 +25,7 @@ ZEND_TLS lexbor_mraw_t lexbor_mraw = {0}; ZEND_TLS lxb_url_parser_t lexbor_parser = {0}; ZEND_TLS lxb_unicode_idna_t lexbor_idna = {0}; +ZEND_TLS uint8_t lexbor_custom_url_map[256] = {0}; static const size_t lexbor_mraw_byte_size = 8192; @@ -549,9 +550,7 @@ static zend_result php_uri_parser_whatwg_fragment_write(void *uri, const zval *v PHP_RINIT_FUNCTION(uri_parser_whatwg) { - lxb_status_t status; - - status = lexbor_mraw_init(&lexbor_mraw, lexbor_mraw_byte_size); + lxb_status_t status = lexbor_mraw_init(&lexbor_mraw, lexbor_mraw_byte_size); if (status != LXB_STATUS_OK) { goto fail; } @@ -566,6 +565,9 @@ PHP_RINIT_FUNCTION(uri_parser_whatwg) goto fail; } + memcpy(lexbor_custom_url_map, lxb_url_get_percent_encoding_map(), sizeof(lexbor_custom_url_map)); + lexbor_custom_url_map['%'] = -1; /* % is percent-encoded */ + return SUCCESS; fail: @@ -653,6 +655,79 @@ static zend_string *php_uri_parser_whatwg_to_string(void *uri, const php_uri_rec return smart_str_extract(&uri_str); } +static zend_string *php_uri_parser_whatwg_percent_encode_component(const char *str, const size_t str_length, const lxb_url_map_type_t map, const bool space_as_plus) +{ + lexbor_str_t lexbor_str = {0}; + + const lexbor_status_t status = lxb_url_percent_encode_utf_8( + (lxb_char_t *) str, str_length, &lexbor_str, lexbor_parser.mraw, lexbor_custom_url_map, map, space_as_plus + ); + + if (status != LXB_STATUS_OK) { + lexbor_str_destroy(&lexbor_str, lexbor_parser.mraw, false); + return NULL; + } + + zend_string *result = zend_string_init((const char *) lexbor_str.data, lexbor_str.length, false); + + lexbor_str_destroy(&lexbor_str, lexbor_parser.mraw, false); + + return result; +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_userinfo_component(const char *str, const size_t str_length) +{ + return php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_USERINFO, false); +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_opaque_host_component(const char *str, const size_t str_length) +{ + return php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_C0, false); +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_path_component(const char *str, const size_t str_length) +{ + return php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_PATH, false); +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_opaque_path_component(const char *str, const size_t str_length) +{ + return php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_C0, false); +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_path_segment_component(const char *str, const size_t str_length) +{ + ZEND_ASSERT((lexbor_custom_url_map['/'] & LXB_URL_MAP_PATH) == 0); + + lexbor_custom_url_map['/'] |= LXB_URL_MAP_PATH; + + zend_string *result = php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_PATH, false); + + lexbor_custom_url_map['/'] &= ~LXB_URL_MAP_PATH; + + return result; +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_query_component(const char *str, const size_t str_length) +{ + return php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_QUERY, false); +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_special_query_component(const char *str, const size_t str_length) +{ + return php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_SPECIAL_QUERY, false); +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_form_query_component(const char *str, const size_t str_length) +{ + return php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_X_WWW_FORM, true); +} + +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_fragment_component(const char *str, const size_t str_length) +{ + return php_uri_parser_whatwg_percent_encode_component(str, str_length, LXB_URL_MAP_FRAGMENT, false); +} + static void php_uri_parser_whatwg_destroy(void *uri) { lxb_url_t *lexbor_uri = uri; diff --git a/ext/uri/uri_parser_whatwg.h b/ext/uri/uri_parser_whatwg.h index 0a03c8e76a93..ee387a6af95d 100644 --- a/ext/uri/uri_parser_whatwg.h +++ b/ext/uri/uri_parser_whatwg.h @@ -30,14 +30,24 @@ ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_scheme(const z ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_host(const zend_string *host); ZEND_ATTRIBUTE_NONNULL zend_result php_uri_parser_whatwg_validate_port(zend_long port); -PHP_RINIT_FUNCTION(uri_parser_whatwg); - ZEND_ATTRIBUTE_NONNULL_ARGS(2, 3, 4, 5, 6, 7, 8, 9) lxb_url_t *php_uri_parser_whatwg_build_from_zval( lxb_url_t *lexbor_base_url, const zval *scheme, const zval *username, const zval *password, const zval *host, const zval *port, const zval *path, const zval *query, const zval *fragment, zval *errors_zv ); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_userinfo_component(const char *str, size_t str_length); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_opaque_host_component(const char *str, size_t str_length); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_path_component(const char *str, size_t str_length); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_opaque_path_component(const char *str, size_t str_length); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_path_segment_component(const char *str, size_t str_length); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_query_component(const char *str, size_t str_length); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_special_query_component(const char *str, size_t str_length); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_form_query_component(const char *str, size_t str_length); +ZEND_ATTRIBUTE_NONNULL zend_string *php_uri_parser_whatwg_percent_encode_fragment_component(const char *str, size_t str_length); + +PHP_RINIT_FUNCTION(uri_parser_whatwg); + ZEND_MODULE_POST_ZEND_DEACTIVATE_D(uri_parser_whatwg); #endif diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index a7a3e340ecf1..15994b60cc70 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -2197,7 +2197,7 @@ PHP_METHOD(ZipArchive, getNameIndex) ZIP_FROM_OBJECT(intern, self); - name = zip_get_name(intern, (int) index, flags); + name = zip_get_name(intern, (zip_uint64_t) index, flags); if (name) { RETVAL_STRING((char *)name); diff --git a/ext/zip/tests/oo_getnameindex_large_index.phpt b/ext/zip/tests/oo_getnameindex_large_index.phpt new file mode 100644 index 000000000000..471dffc38d91 --- /dev/null +++ b/ext/zip/tests/oo_getnameindex_large_index.phpt @@ -0,0 +1,44 @@ +--TEST-- +ZipArchive::getNameIndex() with an index that does not fit in an int +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open($file, ZipArchive::CREATE)) { + exit('failed'); +} + +$zip->addFromString('entry1.txt', 'entry #1'); +$zip->close(); + +if (!$zip->open($file)) { + exit('failed'); +} + +var_dump($zip->getNameIndex(0)); +var_dump($zip->getNameIndex(1 << 32)); +var_dump($zip->getNameIndex((1 << 32) + 1)); +var_dump($zip->getNameIndex(PHP_INT_MAX)); +var_dump($zip->getNameIndex(-1)); + +$zip->close(); +?> +--EXPECT-- +string(10) "entry1.txt" +bool(false) +bool(false) +bool(false) +bool(false) +--CLEAN-- + diff --git a/ext/zip/tests/stream_fstat_unreadable_archive.phpt b/ext/zip/tests/stream_fstat_unreadable_archive.phpt new file mode 100644 index 000000000000..a81fc8fc3cfc --- /dev/null +++ b/ext/zip/tests/stream_fstat_unreadable_archive.phpt @@ -0,0 +1,38 @@ +--TEST-- +fstat() on a zip:// stream whose archive can no longer be opened +--EXTENSIONS-- +zip +--SKIPIF-- + +--FILE-- +open($file, ZipArchive::CREATE)) { + exit('failed'); +} + +$zip->addFromString('entry.txt', 'entry'); +$zip->close(); + +$fp = fopen('zip://' . $file . '#entry.txt', 'rb'); +var_dump($fp !== false); + +file_put_contents($file, 'this is not a zip archive'); + +var_dump(fstat($fp)); + +fclose($fp); +?> +--EXPECT-- +bool(true) +bool(false) +--CLEAN-- + diff --git a/ext/zip/zip_stream.c b/ext/zip/zip_stream.c index a6665630e350..429342b36e3d 100644 --- a/ext/zip/zip_stream.c +++ b/ext/zip/zip_stream.c @@ -192,6 +192,9 @@ static int php_zip_ops_stat(php_stream *stream, php_stream_statbuf *ssb) /* {{{ ssb->sb.st_blocks = -1; #endif ssb->sb.st_ino = -1; + } else { + zend_string_release_ex(file_basename, 0); + return -1; } zend_string_release_ex(file_basename, false); return 0;