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

Closes GH-12437.
Closes GH-11725.
This commit is contained in:
Niels Dossche 2023-10-14 17:57:45 +02:00
parent 2318a81725
commit b34b4d54c3
8 changed files with 154 additions and 14 deletions

1
NEWS
View file

@ -85,6 +85,7 @@ SOAP:
lost the setPersistence()). (nielsdos)
. Fixed bug #49278 (SoapClient::__getLastResponseHeaders returns NULL if
wsdl operation !has output). (nielsdos)
. Fixed bug #44383 (PHP DateTime not converted to xsd:datetime). (nielsdos)
Sockets:
. Removed the deprecated inet_ntoa call support. (David Carlier)

View file

@ -139,6 +139,9 @@ PHP 8.4 UPGRADE NOTES
It is now possible to specify entries in a class map with clark notation
to resolve a type with a specific namespace to a specific class.
For example: '{http://example.com}foo' => 'FooClass'.
. Instances of DateTimeInterface that are passed to xsd:datetime or similar
elements are now serialized as such instead of being serialized as an
empty string.
- XSL:
. It is now possible to use parameters that contain both single and double

View file

@ -56,6 +56,9 @@ PHP 8.4 INTERNALS UPGRADE NOTES
- Added php_libxml_pretend_ctx_error_ex() to emit errors as if they had come
from libxml.
e. ext/date
- Added the php_format_date_ex() API to format instances of php_date_obj.
========================
4. OpCode changes
========================

View file

@ -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;

View file

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

View file

@ -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"
@ -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 *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) {
@ -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) {

View file

@ -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),

View file

@ -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