From 8a80edd0ccc67f8a153fb63192eed502c63cf714 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Sat, 14 Oct 2023 17:57:45 +0200 Subject: [PATCH 1/4] Fix #44383: PHP DateTime not converted to xsd:datetime --- ext/date/php_date.c | 5 ++ ext/date/php_date.h | 1 + ext/soap/php_encoding.c | 36 ++++++--- ext/soap/tests/schema/schema086.phpt | 105 +++++++++++++++++++++++++++ 4 files changed, 136 insertions(+), 11 deletions(-) create mode 100644 ext/soap/tests/schema/schema086.phpt diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 509b09ef37ddd..a92d8b27fb092 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -841,6 +841,11 @@ static zend_string *date_format(const char *format, size_t format_len, timelib_t return string.s; } +PHPAPI zend_string *php_format_date_ex(const char *format, size_t format_len, timelib_time *t, bool localtime) +{ + return date_format(format, format_len, t, localtime); +} + static void php_date(INTERNAL_FUNCTION_PARAMETERS, bool localtime) { zend_string *format; diff --git a/ext/date/php_date.h b/ext/date/php_date.h index a67564476bec7..e01269ef9f318 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -123,6 +123,7 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime); PHPAPI void php_strftime(INTERNAL_FUNCTION_PARAMETERS, bool gm); PHPAPI zend_string *php_format_date(const char *format, size_t format_len, time_t ts, bool localtime); +PHPAPI zend_string *php_format_date_ex(const char *format, size_t format_len, timelib_time *t, bool localtime); /* Mechanism to set new TZ database */ PHPAPI void php_date_set_tzdb(timelib_tzdb *tzdb); diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index 38aaab6c45fbf..aa7ae3c426812 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -21,6 +21,7 @@ #include "php_soap.h" #include "ext/libxml/php_libxml.h" #include "ext/standard/base64.h" +#include "ext/date/php_date.h" #include #include "zend_strtod.h" #include "zend_interfaces.h" @@ -57,7 +58,7 @@ static xmlNodePtr to_xml_list(encodeTypePtr enc, zval *data, int style, xmlNodeP static xmlNodePtr to_xml_list1(encodeTypePtr enc, zval *data, int style, xmlNodePtr parent); /* Datetime encode/decode */ -static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *format, int style, xmlNodePtr parent); +static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *format, const char *ext_date_format, size_t ext_date_format_len, int style, xmlNodePtr parent); static xmlNodePtr to_xml_datetime(encodeTypePtr type, zval *data, int style, xmlNodePtr parent); static xmlNodePtr to_xml_time(encodeTypePtr type, zval *data, int style, xmlNodePtr parent); static xmlNodePtr to_xml_date(encodeTypePtr type, zval *data, int style, xmlNodePtr parent); @@ -2847,7 +2848,7 @@ static zval *guess_zval_convert(zval *ret, encodeTypePtr type, xmlNodePtr data) } /* Time encode/decode */ -static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *format, int style, xmlNodePtr parent) +static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *format, const char *ext_date_format, size_t ext_date_format_len, int style, xmlNodePtr parent) { /* logic hacked from ext/standard/datetime.c */ struct tm *ta, tmbuf; @@ -2905,6 +2906,17 @@ static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *forma efree(buf); } else if (Z_TYPE_P(data) == IS_STRING) { xmlNodeSetContentLen(xmlParam, BAD_CAST(Z_STRVAL_P(data)), Z_STRLEN_P(data)); + } else if (Z_TYPE_P(data) == IS_OBJECT) { + if (instanceof_function_slow(Z_OBJCE_P(data), php_date_get_interface_ce())) { + php_date_obj *dateobj = Z_PHPDATE_P(data); + if (dateobj->time) { + zend_string *formatted = php_format_date_ex(ext_date_format, ext_date_format_len, dateobj->time, dateobj->time->is_localtime); + xmlNodeSetContentLen(xmlParam, BAD_CAST(ZSTR_VAL(formatted)), ZSTR_LEN(formatted)); + zend_string_release_ex(formatted, false); + } else { + soap_error0(E_ERROR, "Encoding: Invalid DateTimeInterface"); + } + } } if (style == SOAP_ENCODED) { @@ -2919,45 +2931,47 @@ static xmlNodePtr to_xml_duration(encodeTypePtr type, zval *data, int style, xml return to_xml_string(type, data, style, parent); } +#define TO_XML_DATETIME_EX_HELPER(type, date, format, ext_date_format, style, parent) \ + to_xml_datetime_ex(type, data, format, ext_date_format, strlen(ext_date_format), style, parent) + static xmlNodePtr to_xml_datetime(encodeTypePtr type, zval *data, int style, xmlNodePtr parent) { - return to_xml_datetime_ex(type, data, "%Y-%m-%dT%H:%M:%S", style, parent); + return TO_XML_DATETIME_EX_HELPER(type, data, "%Y-%m-%dT%H:%M:%S", "Y-m-d\\TH:i:s.up", style, parent); } static xmlNodePtr to_xml_time(encodeTypePtr type, zval *data, int style, xmlNodePtr parent) { - /* TODO: microsecconds */ - return to_xml_datetime_ex(type, data, "%H:%M:%S", style, parent); + return TO_XML_DATETIME_EX_HELPER(type, data, "%H:%M:%S", "H:i:s.up", style, parent); } static xmlNodePtr to_xml_date(encodeTypePtr type, zval *data, int style, xmlNodePtr parent) { - return to_xml_datetime_ex(type, data, "%Y-%m-%d", style, parent); + return TO_XML_DATETIME_EX_HELPER(type, data, "%Y-%m-%d", "Y-m-dp", style, parent); } static xmlNodePtr to_xml_gyearmonth(encodeTypePtr type, zval *data, int style, xmlNodePtr parent) { - return to_xml_datetime_ex(type, data, "%Y-%m", style, parent); + return TO_XML_DATETIME_EX_HELPER(type, data, "%Y-%m", "Y-mp", style, parent); } static xmlNodePtr to_xml_gyear(encodeTypePtr type, zval *data, int style, xmlNodePtr parent) { - return to_xml_datetime_ex(type, data, "%Y", style, parent); + return TO_XML_DATETIME_EX_HELPER(type, data, "%Y", "Yp", style, parent); } static xmlNodePtr to_xml_gmonthday(encodeTypePtr type, zval *data, int style, xmlNodePtr parent) { - return to_xml_datetime_ex(type, data, "--%m-%d", style, parent); + return TO_XML_DATETIME_EX_HELPER(type, data, "--%m-%d", "--m-dp", style, parent); } static xmlNodePtr to_xml_gday(encodeTypePtr type, zval *data, int style, xmlNodePtr parent) { - return to_xml_datetime_ex(type, data, "---%d", style, parent); + return TO_XML_DATETIME_EX_HELPER(type, data, "---%d", "---dp", style, parent); } static xmlNodePtr to_xml_gmonth(encodeTypePtr type, zval *data, int style, xmlNodePtr parent) { - return to_xml_datetime_ex(type, data, "--%m--", style, parent); + return TO_XML_DATETIME_EX_HELPER(type, data, "--%m--", "--m--p", style, parent); } static zval* to_zval_list(zval *ret, encodeTypePtr enc, xmlNodePtr data) { diff --git a/ext/soap/tests/schema/schema086.phpt b/ext/soap/tests/schema/schema086.phpt new file mode 100644 index 0000000000000..4a770056c6fcf --- /dev/null +++ b/ext/soap/tests/schema/schema086.phpt @@ -0,0 +1,105 @@ +--TEST-- +SOAP XML Schema 86: DateTimeInterface date/time types +--EXTENSIONS-- +soap +xml +--FILE-- + + + + + + + + + + + + +EOF; + +$test_dates = [ + new DateTime("2023-10-14 13:37:42.1234+02:00"), + new DateTimeImmutable("2023-10-14 13:37:42.1234+02:00"), + new DateTime("2023-10-14 13:37:42.1234Z"), +]; + +foreach ($test_dates as $date) { + test_schema($schema,'type="tns:testType"',array( + 'dateTime' => $date, + 'time' => $date, + 'date' => $date, + 'gYearMonth' => $date, + 'gYear' => $date, + 'gMonthDay' => $date, + 'gDay' => $date, + 'gMonth' => $date + )); +} +echo "ok"; +?> +--EXPECTF-- + +2023-10-14T13:37:42.123400+02:002023-10-14+02:002023-10+02:002023+02:00--10-14+02:00---14+02:00--10--+02:00 +object(stdClass)#%d (8) { + ["dateTime"]=> + string(32) "2023-10-14T13:37:42.123400+02:00" + ["time"]=> + string(21) "13:37:42.123400+02:00" + ["date"]=> + string(16) "2023-10-14+02:00" + ["gYearMonth"]=> + string(13) "2023-10+02:00" + ["gYear"]=> + string(10) "2023+02:00" + ["gMonthDay"]=> + string(13) "--10-14+02:00" + ["gDay"]=> + string(11) "---14+02:00" + ["gMonth"]=> + string(12) "--10--+02:00" +} + +2023-10-14T13:37:42.123400+02:002023-10-14+02:002023-10+02:002023+02:00--10-14+02:00---14+02:00--10--+02:00 +object(stdClass)#9 (8) { + ["dateTime"]=> + string(32) "2023-10-14T13:37:42.123400+02:00" + ["time"]=> + string(21) "13:37:42.123400+02:00" + ["date"]=> + string(16) "2023-10-14+02:00" + ["gYearMonth"]=> + string(13) "2023-10+02:00" + ["gYear"]=> + string(10) "2023+02:00" + ["gMonthDay"]=> + string(13) "--10-14+02:00" + ["gDay"]=> + string(11) "---14+02:00" + ["gMonth"]=> + string(12) "--10--+02:00" +} + +2023-10-14T13:37:42.123400Z2023-10-14Z2023-10Z2023Z--10-14Z---14Z--10--Z +object(stdClass)#8 (8) { + ["dateTime"]=> + string(27) "2023-10-14T13:37:42.123400Z" + ["time"]=> + string(16) "13:37:42.123400Z" + ["date"]=> + string(11) "2023-10-14Z" + ["gYearMonth"]=> + string(8) "2023-10Z" + ["gYear"]=> + string(5) "2023Z" + ["gMonthDay"]=> + string(8) "--10-14Z" + ["gDay"]=> + string(6) "---14Z" + ["gMonth"]=> + string(7) "--10--Z" +} +ok From c656146a7ccb20cf96e376a4e77ec7d8fdbc1737 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Wed, 22 Nov 2023 19:37:27 +0100 Subject: [PATCH 2/4] Change date API to use php_date_obj instead of timelib_time --- ext/date/php_date.c | 4 ++-- ext/date/php_date.h | 2 +- ext/soap/php_encoding.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index a92d8b27fb092..0cc84b415b364 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -841,9 +841,9 @@ static zend_string *date_format(const char *format, size_t format_len, timelib_t return string.s; } -PHPAPI zend_string *php_format_date_ex(const char *format, size_t format_len, timelib_time *t, bool localtime) +PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, php_date_obj *date_obj) { - return date_format(format, format_len, t, localtime); + return date_format(format, format_len, date_obj->time, date_obj->time->is_localtime); } static void php_date(INTERNAL_FUNCTION_PARAMETERS, bool localtime) diff --git a/ext/date/php_date.h b/ext/date/php_date.h index e01269ef9f318..97f4bcc0a843f 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -123,7 +123,7 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime); PHPAPI void php_strftime(INTERNAL_FUNCTION_PARAMETERS, bool gm); PHPAPI zend_string *php_format_date(const char *format, size_t format_len, time_t ts, bool localtime); -PHPAPI zend_string *php_format_date_ex(const char *format, size_t format_len, timelib_time *t, bool localtime); +PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, php_date_obj *date_obj); /* Mechanism to set new TZ database */ PHPAPI void php_date_set_tzdb(timelib_tzdb *tzdb); diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index aa7ae3c426812..c4479fddd4dab 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2910,7 +2910,7 @@ static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *forma if (instanceof_function_slow(Z_OBJCE_P(data), php_date_get_interface_ce())) { php_date_obj *dateobj = Z_PHPDATE_P(data); if (dateobj->time) { - zend_string *formatted = php_format_date_ex(ext_date_format, ext_date_format_len, dateobj->time, dateobj->time->is_localtime); + zend_string *formatted = php_format_date_obj(ext_date_format, ext_date_format_len, dateobj); xmlNodeSetContentLen(xmlParam, BAD_CAST(ZSTR_VAL(formatted)), ZSTR_LEN(formatted)); zend_string_release_ex(formatted, false); } else { From 4c2fc035ebe104ae58668782be5e6093795c2189 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 24 Nov 2023 00:00:11 +0100 Subject: [PATCH 3/4] Core review comments --- ext/date/php_date.c | 4 ++++ ext/soap/php_encoding.c | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 0cc84b415b364..6f5937f3575eb 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -843,6 +843,10 @@ static zend_string *date_format(const char *format, size_t format_len, timelib_t PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, php_date_obj *date_obj) { + if (!date_obj->time) { + return NULL; + } + return date_format(format, format_len, date_obj->time, date_obj->time->is_localtime); } diff --git a/ext/soap/php_encoding.c b/ext/soap/php_encoding.c index c4479fddd4dab..71bfb9618263e 100644 --- a/ext/soap/php_encoding.c +++ b/ext/soap/php_encoding.c @@ -2908,11 +2908,11 @@ static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *forma xmlNodeSetContentLen(xmlParam, BAD_CAST(Z_STRVAL_P(data)), Z_STRLEN_P(data)); } else if (Z_TYPE_P(data) == IS_OBJECT) { if (instanceof_function_slow(Z_OBJCE_P(data), php_date_get_interface_ce())) { - php_date_obj *dateobj = Z_PHPDATE_P(data); - if (dateobj->time) { - zend_string *formatted = php_format_date_obj(ext_date_format, ext_date_format_len, dateobj); - xmlNodeSetContentLen(xmlParam, BAD_CAST(ZSTR_VAL(formatted)), ZSTR_LEN(formatted)); - zend_string_release_ex(formatted, false); + php_date_obj *date_obj = Z_PHPDATE_P(data); + zend_string *formatted_date_string = php_format_date_obj(ext_date_format, ext_date_format_len, date_obj); + if (formatted_date_string) { + xmlNodeSetContentLen(xmlParam, BAD_CAST(ZSTR_VAL(formatted_date_string)), ZSTR_LEN(formatted_date_string)); + zend_string_release_ex(formatted_date_string, false); } else { soap_error0(E_ERROR, "Encoding: Invalid DateTimeInterface"); } From 25614d3a768656e2351de0982dd8f1217fc7a8f6 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Fri, 24 Nov 2023 11:52:54 +0100 Subject: [PATCH 4/4] Add soap module dependencies --- ext/soap/soap.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ext/soap/soap.c b/ext/soap/soap.c index d28c77ca9bb68..9064d1cbacef0 100644 --- a/ext/soap/soap.c +++ b/ext/soap/soap.c @@ -215,10 +215,14 @@ PHP_MINIT_FUNCTION(soap); PHP_MSHUTDOWN_FUNCTION(soap); PHP_MINFO_FUNCTION(soap); +static const zend_module_dep soap_deps[] = { + ZEND_MOD_REQUIRED("date") + ZEND_MOD_END +}; + zend_module_entry soap_module_entry = { -#ifdef STANDARD_MODULE_HEADER - STANDARD_MODULE_HEADER, -#endif + STANDARD_MODULE_HEADER_EX, NULL, + soap_deps, "soap", ext_functions, PHP_MINIT(soap),