@@ -352,6 +352,7 @@ static HashTable *date_object_get_gc(zend_object *object, zval **table, int *n);
352352static HashTable * date_object_get_properties_for (zend_object * object , zend_prop_purpose purpose );
353353static HashTable * date_object_get_gc_interval (zend_object * object , zval * * table , int * n );
354354static HashTable * date_object_get_properties_interval (zend_object * object );
355+ static HashTable * date_object_get_properties_for_interval (zend_object * object , zend_prop_purpose purpose );
355356static HashTable * date_object_get_gc_period (zend_object * object , zval * * table , int * n );
356357static HashTable * date_object_get_properties_for_timezone (zend_object * object , zend_prop_purpose purpose );
357358static HashTable * date_object_get_gc_timezone (zend_object * object , zval * * table , int * n );
@@ -396,6 +397,7 @@ static PHP_GINIT_FUNCTION(date)
396397 date_globals -> default_timezone = NULL ;
397398 date_globals -> timezone = NULL ;
398399 date_globals -> tzcache = NULL ;
400+ date_globals -> interval_props_cache = NULL ;
399401}
400402/* }}} */
401403
@@ -407,6 +409,13 @@ static void _php_date_tzinfo_dtor(zval *zv) /* {{{ */
407409 timelib_tzinfo_dtor (tzi );
408410} /* }}} */
409411
412+ static void _php_date_interval_props_dtor (zval * zv ) /* {{{ */
413+ {
414+ HashTable * props = (HashTable * )Z_PTR_P (zv );
415+
416+ zend_hash_release (props );
417+ } /* }}} */
418+
410419/* {{{ PHP_RINIT_FUNCTION */
411420PHP_RINIT_FUNCTION (date )
412421{
@@ -416,6 +425,7 @@ PHP_RINIT_FUNCTION(date)
416425 DATEG (timezone ) = NULL ;
417426 DATEG (tzcache ) = NULL ;
418427 DATEG (last_errors ) = NULL ;
428+ DATEG (interval_props_cache ) = NULL ;
419429
420430 return SUCCESS ;
421431}
@@ -441,6 +451,12 @@ ZEND_MODULE_POST_ZEND_DEACTIVATE_D(date)
441451 DATEG (tzcache ) = NULL ;
442452 }
443453
454+ if (DATEG (interval_props_cache )) {
455+ zend_hash_destroy (DATEG (interval_props_cache ));
456+ FREE_HASHTABLE (DATEG (interval_props_cache ));
457+ DATEG (interval_props_cache ) = NULL ;
458+ }
459+
444460 if (DATEG (last_errors )) {
445461 timelib_error_container_dtor (DATEG (last_errors ));
446462 DATEG (last_errors ) = NULL ;
@@ -1816,6 +1832,7 @@ static void date_register_classes(void) /* {{{ */
18161832 date_object_handlers_interval .read_property = date_interval_read_property ;
18171833 date_object_handlers_interval .write_property = date_interval_write_property ;
18181834 date_object_handlers_interval .get_properties = date_object_get_properties_interval ;
1835+ date_object_handlers_interval .get_properties_for = date_object_get_properties_for_interval ;
18191836 date_object_handlers_interval .get_property_ptr_ptr = date_interval_get_property_ptr_ptr ;
18201837 date_object_handlers_interval .get_gc = date_object_get_gc_interval ;
18211838 date_object_handlers_interval .compare = date_interval_compare_objects ;
@@ -2240,6 +2257,54 @@ static HashTable *date_object_get_properties_interval(zend_object *object) /* {{
22402257 return props ;
22412258} /* }}} */
22422259
2260+ static HashTable * date_object_get_properties_for_interval (zend_object * object , zend_prop_purpose purpose ) /* {{{ */
2261+ {
2262+ HashTable * props ;
2263+ HashTable * cached ;
2264+ php_interval_obj * intervalobj ;
2265+ zend_ulong handle = object -> handle ;
2266+
2267+ switch (purpose ) {
2268+ case ZEND_PROP_PURPOSE_DEBUG :
2269+ case ZEND_PROP_PURPOSE_SERIALIZE :
2270+ case ZEND_PROP_PURPOSE_VAR_EXPORT :
2271+ case ZEND_PROP_PURPOSE_JSON :
2272+ case ZEND_PROP_PURPOSE_ARRAY_CAST :
2273+ break ;
2274+ default :
2275+ return zend_std_get_properties_for (object , purpose );
2276+ }
2277+
2278+ intervalobj = php_interval_obj_from_obj (object );
2279+
2280+ if (!intervalobj -> initialized ) {
2281+ return zend_array_dup (zend_std_get_properties (object ));
2282+ }
2283+
2284+ /* Lazily allocate the cache HashTable */
2285+ if (!DATEG (interval_props_cache )) {
2286+ ALLOC_HASHTABLE (DATEG (interval_props_cache ));
2287+ zend_hash_init (DATEG (interval_props_cache ), 8 , NULL , _php_date_interval_props_dtor , 0 );
2288+ }
2289+
2290+ /* If cache exists and is actively in use (refcount > 1), we're in a recursive
2291+ * call (e.g., circular reference during json_encode). Return the same cache
2292+ * so that circular reference detection works correctly. */
2293+ cached = zend_hash_index_find_ptr (DATEG (interval_props_cache ), handle );
2294+ if (cached && GC_REFCOUNT (cached ) > 1 ) {
2295+ GC_ADDREF (cached );
2296+ return cached ;
2297+ }
2298+
2299+ /* Create new cache or replace stale one */
2300+ props = zend_array_dup (zend_std_get_properties (object ));
2301+ date_interval_object_to_hash (intervalobj , props );
2302+ zend_hash_index_update_ptr (DATEG (interval_props_cache ), handle , props );
2303+
2304+ GC_ADDREF (props );
2305+ return props ;
2306+ } /* }}} */
2307+
22432308static zend_object * date_object_new_period (zend_class_entry * class_type ) /* {{{ */
22442309{
22452310 php_period_obj * intern = zend_object_alloc (sizeof (php_period_obj ), class_type );
@@ -2306,6 +2371,10 @@ static void date_object_free_storage_interval(zend_object *object) /* {{{ */
23062371 zend_string_release (intern -> date_string );
23072372 intern -> date_string = NULL ;
23082373 }
2374+ /* Clean up cached properties for this object */
2375+ if (DATEG (interval_props_cache )) {
2376+ zend_hash_index_del (DATEG (interval_props_cache ), object -> handle );
2377+ }
23092378 timelib_rel_time_dtor (intern -> diff );
23102379 zend_object_std_dtor (& intern -> std );
23112380} /* }}} */
0 commit comments