@@ -77,20 +77,24 @@ static inline php_date_time_duration *create_duration_shell(zval *target)
7777 return Z_DATE_TIME_DURATION_P (target );
7878}
7979
80- ZEND_ATTRIBUTE_NODISCARD static inline zend_result sync_properties ( php_date_time_duration * object )
80+ static inline bool duration_representable ( const timelib_duration * duration )
8181{
82- if (
82+ return
8383 /* Check if the duration would overflow the $seconds property. */
84- object -> duration . seconds > ((uint64_t )ZEND_LONG_MAX )
84+ duration -> seconds <= ((uint64_t )ZEND_LONG_MAX )
8585 /* This constraint is an explicit part of PHP's API: It is the maximum $seconds
8686 * value that allows storing the entire duration as a single int64_t counting
8787 * nanoseconds, which might be desirable in the future when userland `int` is
8888 * consistently 64 bits.
8989 *
9090 * While it is currently also enforced by timelib, this might change
9191 * in a future version of timelib, thus we also enforce it manually. */
92- || object -> duration .seconds > UINT64_C (9223372035 )
93- ) {
92+ && duration -> seconds <= UINT64_C (9223372035 );
93+ }
94+
95+ ZEND_ATTRIBUTE_NODISCARD static inline zend_result sync_properties (php_date_time_duration * object )
96+ {
97+ if (!duration_representable (& object -> duration )) {
9498 throw_out_of_range_exception ();
9599 return FAILURE ;
96100 }
@@ -149,6 +153,52 @@ PHP_METHOD(Time_Duration, __construct)
149153 zend_throw_error (NULL , "Cannot directly construct Time\\Duration, use Time\\Duration::from*() methods instead" );
150154}
151155
156+ PHP_METHOD (Time_Duration , __unserialize )
157+ {
158+ php_date_time_duration * duration = Z_DATE_TIME_DURATION_P (ZEND_THIS );
159+
160+ HashTable * data ;
161+
162+ ZEND_PARSE_PARAMETERS_START (1 , 1 )
163+ Z_PARAM_ARRAY_HT (data );
164+ ZEND_PARSE_PARAMETERS_END ();
165+
166+ object_properties_load (& duration -> std , data );
167+ if (EG (exception )) {
168+ goto fail ;
169+ }
170+
171+ zval * seconds = OBJ_PROP_NUM (& duration -> std , 0 );
172+ zval * nanoseconds = OBJ_PROP_NUM (& duration -> std , 1 );
173+ zval * negative = OBJ_PROP_NUM (& duration -> std , 2 );
174+
175+ /* Verify that both properties are positive, since the timelib_duration_ctor_static() takes unsigned. */
176+ if (Z_LVAL_P (seconds ) < 0 || Z_LVAL_P (nanoseconds ) < 0 ) {
177+ goto fail ;
178+ }
179+
180+ int error = timelib_duration_ctor_static (& duration -> duration , Z_LVAL_P (seconds ), Z_LVAL_P (nanoseconds ), Z_TYPE_P (negative ) == IS_TRUE );
181+ if (error != TIMELIB_ERROR_NO_ERROR ) {
182+ throw_timelib_error (error );
183+ goto fail ;
184+ }
185+
186+ if (!duration_representable (& duration -> duration )) {
187+ throw_out_of_range_exception ();
188+ goto fail ;
189+ }
190+
191+ return ;
192+
193+ fail :
194+
195+ /* If an exception is already active (e.g. for unrepresentable durations) it will be wrapped for
196+ * uniform exceptions thrown from unserialization handlers, but to still provide additional
197+ * context for a human reader. */
198+ zend_throw_exception_ex (NULL , 0 , "Invalid serialization data for %s object" , ZSTR_VAL (duration -> std .ce -> name ));
199+ RETURN_THROWS ();
200+ }
201+
152202PHP_METHOD (Time_Duration , fromSeconds )
153203{
154204 zend_ulong seconds ;
0 commit comments