Skip to content

Fix #44383: PHP DateTime not converted to xsd:datetime #12437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions ext/date/php_date.c
Original file line number Diff line number Diff line change
Expand Up @@ -841,6 +841,15 @@ static zend_string *date_format(const char *format, size_t format_len, timelib_t
return string.s;
}

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);
}

static void php_date(INTERNAL_FUNCTION_PARAMETERS, bool localtime)
{
zend_string *format;
Expand Down
1 change: 1 addition & 0 deletions ext/date/php_date.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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);
Expand Down
36 changes: 25 additions & 11 deletions ext/soap/php_encoding.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 <libxml/parserInternals.h>
#include "zend_strtod.h"
#include "zend_interfaces.h"
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 *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");
}
}
}

if (style == SOAP_ENCODED) {
Expand All @@ -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) {
Expand Down
10 changes: 7 additions & 3 deletions ext/soap/soap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
105 changes: 105 additions & 0 deletions ext/soap/tests/schema/schema086.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
--TEST--
SOAP XML Schema 86: DateTimeInterface date/time types
--EXTENSIONS--
soap
xml
--FILE--
<?php
include "test_schema.inc";
$schema = <<<EOF
<complexType name="testType">
<sequence>
<element name="dateTime" type="dateTime"/>
<element name="time" type="time"/>
<element name="date" type="date"/>
<element name="gYearMonth" type="gYearMonth"/>
<element name="gYear" type="gYear"/>
<element name="gMonthDay" type="gMonthDay"/>
<element name="gDay" type="gDay"/>
<element name="gMonth" type="gMonth"/>
</sequence>
</complexType>
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--
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test-uri/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:test><testParam xsi:type="ns1:testType"><dateTime xsi:type="xsd:dateTime">2023-10-14T13:37:42.123400+02:00</dateTime><time xsi:type="xsd:time">13:37:42.123400+02:00</time><date xsi:type="xsd:date">2023-10-14+02:00</date><gYearMonth xsi:type="xsd:gYearMonth">2023-10+02:00</gYearMonth><gYear xsi:type="xsd:gYear">2023+02:00</gYear><gMonthDay xsi:type="xsd:gMonthDay">--10-14+02:00</gMonthDay><gDay xsi:type="xsd:gDay">---14+02:00</gDay><gMonth xsi:type="xsd:gMonth">--10--+02:00</gMonth></testParam></ns1:test></SOAP-ENV:Body></SOAP-ENV:Envelope>
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"
}
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test-uri/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:test><testParam xsi:type="ns1:testType"><dateTime xsi:type="xsd:dateTime">2023-10-14T13:37:42.123400+02:00</dateTime><time xsi:type="xsd:time">13:37:42.123400+02:00</time><date xsi:type="xsd:date">2023-10-14+02:00</date><gYearMonth xsi:type="xsd:gYearMonth">2023-10+02:00</gYearMonth><gYear xsi:type="xsd:gYear">2023+02:00</gYear><gMonthDay xsi:type="xsd:gMonthDay">--10-14+02:00</gMonthDay><gDay xsi:type="xsd:gDay">---14+02:00</gDay><gMonth xsi:type="xsd:gMonth">--10--+02:00</gMonth></testParam></ns1:test></SOAP-ENV:Body></SOAP-ENV:Envelope>
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"
}
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test-uri/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><ns1:test><testParam xsi:type="ns1:testType"><dateTime xsi:type="xsd:dateTime">2023-10-14T13:37:42.123400Z</dateTime><time xsi:type="xsd:time">13:37:42.123400Z</time><date xsi:type="xsd:date">2023-10-14Z</date><gYearMonth xsi:type="xsd:gYearMonth">2023-10Z</gYearMonth><gYear xsi:type="xsd:gYear">2023Z</gYear><gMonthDay xsi:type="xsd:gMonthDay">--10-14Z</gMonthDay><gDay xsi:type="xsd:gDay">---14Z</gDay><gMonth xsi:type="xsd:gMonth">--10--Z</gMonth></testParam></ns1:test></SOAP-ENV:Body></SOAP-ENV:Envelope>
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