Skip to content

Commit b34b4d5

Browse files
committed
Fix #44383: PHP DateTime not converted to xsd:datetime
Closes GH-12437. Closes GH-11725.
1 parent 2318a81 commit b34b4d5

File tree

8 files changed

+154
-14
lines changed

8 files changed

+154
-14
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ SOAP:
8585
lost the setPersistence()). (nielsdos)
8686
. Fixed bug #49278 (SoapClient::__getLastResponseHeaders returns NULL if
8787
wsdl operation !has output). (nielsdos)
88+
. Fixed bug #44383 (PHP DateTime not converted to xsd:datetime). (nielsdos)
8889

8990
Sockets:
9091
. Removed the deprecated inet_ntoa call support. (David Carlier)

UPGRADING

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ PHP 8.4 UPGRADE NOTES
139139
It is now possible to specify entries in a class map with clark notation
140140
to resolve a type with a specific namespace to a specific class.
141141
For example: '{http://example.com}foo' => 'FooClass'.
142+
. Instances of DateTimeInterface that are passed to xsd:datetime or similar
143+
elements are now serialized as such instead of being serialized as an
144+
empty string.
142145

143146
- XSL:
144147
. It is now possible to use parameters that contain both single and double

UPGRADING.INTERNALS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ PHP 8.4 INTERNALS UPGRADE NOTES
5656
- Added php_libxml_pretend_ctx_error_ex() to emit errors as if they had come
5757
from libxml.
5858

59+
e. ext/date
60+
- Added the php_format_date_ex() API to format instances of php_date_obj.
61+
5962
========================
6063
4. OpCode changes
6164
========================

ext/date/php_date.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,15 @@ static zend_string *date_format(const char *format, size_t format_len, timelib_t
841841
return string.s;
842842
}
843843

844+
PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, php_date_obj *date_obj)
845+
{
846+
if (!date_obj->time) {
847+
return NULL;
848+
}
849+
850+
return date_format(format, format_len, date_obj->time, date_obj->time->is_localtime);
851+
}
852+
844853
static void php_date(INTERNAL_FUNCTION_PARAMETERS, bool localtime)
845854
{
846855
zend_string *format;

ext/date/php_date.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ PHPAPI int php_idate(char format, time_t ts, bool localtime);
123123

124124
PHPAPI void php_strftime(INTERNAL_FUNCTION_PARAMETERS, bool gm);
125125
PHPAPI zend_string *php_format_date(const char *format, size_t format_len, time_t ts, bool localtime);
126+
PHPAPI zend_string *php_format_date_obj(const char *format, size_t format_len, php_date_obj *date_obj);
126127

127128
/* Mechanism to set new TZ database */
128129
PHPAPI void php_date_set_tzdb(timelib_tzdb *tzdb);

ext/soap/php_encoding.c

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "php_soap.h"
2222
#include "ext/libxml/php_libxml.h"
2323
#include "ext/standard/base64.h"
24+
#include "ext/date/php_date.h"
2425
#include <libxml/parserInternals.h>
2526
#include "zend_strtod.h"
2627
#include "zend_interfaces.h"
@@ -57,7 +58,7 @@ static xmlNodePtr to_xml_list(encodeTypePtr enc, zval *data, int style, xmlNodeP
5758
static xmlNodePtr to_xml_list1(encodeTypePtr enc, zval *data, int style, xmlNodePtr parent);
5859

5960
/* Datetime encode/decode */
60-
static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *format, int style, xmlNodePtr parent);
61+
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);
6162
static xmlNodePtr to_xml_datetime(encodeTypePtr type, zval *data, int style, xmlNodePtr parent);
6263
static xmlNodePtr to_xml_time(encodeTypePtr type, zval *data, int style, xmlNodePtr parent);
6364
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)
28472848
}
28482849

28492850
/* Time encode/decode */
2850-
static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *format, int style, xmlNodePtr parent)
2851+
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)
28512852
{
28522853
/* logic hacked from ext/standard/datetime.c */
28532854
struct tm *ta, tmbuf;
@@ -2905,6 +2906,17 @@ static xmlNodePtr to_xml_datetime_ex(encodeTypePtr type, zval *data, char *forma
29052906
efree(buf);
29062907
} else if (Z_TYPE_P(data) == IS_STRING) {
29072908
xmlNodeSetContentLen(xmlParam, BAD_CAST(Z_STRVAL_P(data)), Z_STRLEN_P(data));
2909+
} else if (Z_TYPE_P(data) == IS_OBJECT) {
2910+
if (instanceof_function_slow(Z_OBJCE_P(data), php_date_get_interface_ce())) {
2911+
php_date_obj *date_obj = Z_PHPDATE_P(data);
2912+
zend_string *formatted_date_string = php_format_date_obj(ext_date_format, ext_date_format_len, date_obj);
2913+
if (formatted_date_string) {
2914+
xmlNodeSetContentLen(xmlParam, BAD_CAST(ZSTR_VAL(formatted_date_string)), ZSTR_LEN(formatted_date_string));
2915+
zend_string_release_ex(formatted_date_string, false);
2916+
} else {
2917+
soap_error0(E_ERROR, "Encoding: Invalid DateTimeInterface");
2918+
}
2919+
}
29082920
}
29092921

29102922
if (style == SOAP_ENCODED) {
@@ -2919,45 +2931,47 @@ static xmlNodePtr to_xml_duration(encodeTypePtr type, zval *data, int style, xml
29192931
return to_xml_string(type, data, style, parent);
29202932
}
29212933

2934+
#define TO_XML_DATETIME_EX_HELPER(type, date, format, ext_date_format, style, parent) \
2935+
to_xml_datetime_ex(type, data, format, ext_date_format, strlen(ext_date_format), style, parent)
2936+
29222937
static xmlNodePtr to_xml_datetime(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
29232938
{
2924-
return to_xml_datetime_ex(type, data, "%Y-%m-%dT%H:%M:%S", style, parent);
2939+
return TO_XML_DATETIME_EX_HELPER(type, data, "%Y-%m-%dT%H:%M:%S", "Y-m-d\\TH:i:s.up", style, parent);
29252940
}
29262941

29272942
static xmlNodePtr to_xml_time(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
29282943
{
2929-
/* TODO: microsecconds */
2930-
return to_xml_datetime_ex(type, data, "%H:%M:%S", style, parent);
2944+
return TO_XML_DATETIME_EX_HELPER(type, data, "%H:%M:%S", "H:i:s.up", style, parent);
29312945
}
29322946

29332947
static xmlNodePtr to_xml_date(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
29342948
{
2935-
return to_xml_datetime_ex(type, data, "%Y-%m-%d", style, parent);
2949+
return TO_XML_DATETIME_EX_HELPER(type, data, "%Y-%m-%d", "Y-m-dp", style, parent);
29362950
}
29372951

29382952
static xmlNodePtr to_xml_gyearmonth(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
29392953
{
2940-
return to_xml_datetime_ex(type, data, "%Y-%m", style, parent);
2954+
return TO_XML_DATETIME_EX_HELPER(type, data, "%Y-%m", "Y-mp", style, parent);
29412955
}
29422956

29432957
static xmlNodePtr to_xml_gyear(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
29442958
{
2945-
return to_xml_datetime_ex(type, data, "%Y", style, parent);
2959+
return TO_XML_DATETIME_EX_HELPER(type, data, "%Y", "Yp", style, parent);
29462960
}
29472961

29482962
static xmlNodePtr to_xml_gmonthday(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
29492963
{
2950-
return to_xml_datetime_ex(type, data, "--%m-%d", style, parent);
2964+
return TO_XML_DATETIME_EX_HELPER(type, data, "--%m-%d", "--m-dp", style, parent);
29512965
}
29522966

29532967
static xmlNodePtr to_xml_gday(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
29542968
{
2955-
return to_xml_datetime_ex(type, data, "---%d", style, parent);
2969+
return TO_XML_DATETIME_EX_HELPER(type, data, "---%d", "---dp", style, parent);
29562970
}
29572971

29582972
static xmlNodePtr to_xml_gmonth(encodeTypePtr type, zval *data, int style, xmlNodePtr parent)
29592973
{
2960-
return to_xml_datetime_ex(type, data, "--%m--", style, parent);
2974+
return TO_XML_DATETIME_EX_HELPER(type, data, "--%m--", "--m--p", style, parent);
29612975
}
29622976

29632977
static zval* to_zval_list(zval *ret, encodeTypePtr enc, xmlNodePtr data) {

ext/soap/soap.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,14 @@ PHP_MINIT_FUNCTION(soap);
215215
PHP_MSHUTDOWN_FUNCTION(soap);
216216
PHP_MINFO_FUNCTION(soap);
217217

218+
static const zend_module_dep soap_deps[] = {
219+
ZEND_MOD_REQUIRED("date")
220+
ZEND_MOD_END
221+
};
222+
218223
zend_module_entry soap_module_entry = {
219-
#ifdef STANDARD_MODULE_HEADER
220-
STANDARD_MODULE_HEADER,
221-
#endif
224+
STANDARD_MODULE_HEADER_EX, NULL,
225+
soap_deps,
222226
"soap",
223227
ext_functions,
224228
PHP_MINIT(soap),

ext/soap/tests/schema/schema086.phpt

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
--TEST--
2+
SOAP XML Schema 86: DateTimeInterface date/time types
3+
--EXTENSIONS--
4+
soap
5+
xml
6+
--FILE--
7+
<?php
8+
include "test_schema.inc";
9+
$schema = <<<EOF
10+
<complexType name="testType">
11+
<sequence>
12+
<element name="dateTime" type="dateTime"/>
13+
<element name="time" type="time"/>
14+
<element name="date" type="date"/>
15+
<element name="gYearMonth" type="gYearMonth"/>
16+
<element name="gYear" type="gYear"/>
17+
<element name="gMonthDay" type="gMonthDay"/>
18+
<element name="gDay" type="gDay"/>
19+
<element name="gMonth" type="gMonth"/>
20+
</sequence>
21+
</complexType>
22+
EOF;
23+
24+
$test_dates = [
25+
new DateTime("2023-10-14 13:37:42.1234+02:00"),
26+
new DateTimeImmutable("2023-10-14 13:37:42.1234+02:00"),
27+
new DateTime("2023-10-14 13:37:42.1234Z"),
28+
];
29+
30+
foreach ($test_dates as $date) {
31+
test_schema($schema,'type="tns:testType"',array(
32+
'dateTime' => $date,
33+
'time' => $date,
34+
'date' => $date,
35+
'gYearMonth' => $date,
36+
'gYear' => $date,
37+
'gMonthDay' => $date,
38+
'gDay' => $date,
39+
'gMonth' => $date
40+
));
41+
}
42+
echo "ok";
43+
?>
44+
--EXPECTF--
45+
<?xml version="1.0" encoding="UTF-8"?>
46+
<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>
47+
object(stdClass)#%d (8) {
48+
["dateTime"]=>
49+
string(32) "2023-10-14T13:37:42.123400+02:00"
50+
["time"]=>
51+
string(21) "13:37:42.123400+02:00"
52+
["date"]=>
53+
string(16) "2023-10-14+02:00"
54+
["gYearMonth"]=>
55+
string(13) "2023-10+02:00"
56+
["gYear"]=>
57+
string(10) "2023+02:00"
58+
["gMonthDay"]=>
59+
string(13) "--10-14+02:00"
60+
["gDay"]=>
61+
string(11) "---14+02:00"
62+
["gMonth"]=>
63+
string(12) "--10--+02:00"
64+
}
65+
<?xml version="1.0" encoding="UTF-8"?>
66+
<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>
67+
object(stdClass)#9 (8) {
68+
["dateTime"]=>
69+
string(32) "2023-10-14T13:37:42.123400+02:00"
70+
["time"]=>
71+
string(21) "13:37:42.123400+02:00"
72+
["date"]=>
73+
string(16) "2023-10-14+02:00"
74+
["gYearMonth"]=>
75+
string(13) "2023-10+02:00"
76+
["gYear"]=>
77+
string(10) "2023+02:00"
78+
["gMonthDay"]=>
79+
string(13) "--10-14+02:00"
80+
["gDay"]=>
81+
string(11) "---14+02:00"
82+
["gMonth"]=>
83+
string(12) "--10--+02:00"
84+
}
85+
<?xml version="1.0" encoding="UTF-8"?>
86+
<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>
87+
object(stdClass)#8 (8) {
88+
["dateTime"]=>
89+
string(27) "2023-10-14T13:37:42.123400Z"
90+
["time"]=>
91+
string(16) "13:37:42.123400Z"
92+
["date"]=>
93+
string(11) "2023-10-14Z"
94+
["gYearMonth"]=>
95+
string(8) "2023-10Z"
96+
["gYear"]=>
97+
string(5) "2023Z"
98+
["gMonthDay"]=>
99+
string(8) "--10-14Z"
100+
["gDay"]=>
101+
string(6) "---14Z"
102+
["gMonth"]=>
103+
string(7) "--10--Z"
104+
}
105+
ok

0 commit comments

Comments
 (0)