@@ -496,6 +496,21 @@ impl Zval {
496496 /// * `val` - The value to set the zval as.
497497 /// * `persistent` - Whether the string should persist between requests.
498498 ///
499+ /// # Persistent Strings
500+ ///
501+ /// When `persistent` is `true`, the string is allocated from PHP's
502+ /// persistent heap (using `malloc`) rather than the request-bound heap.
503+ /// This is typically used for strings that need to survive across multiple
504+ /// PHP requests, such as class names, function names, or module-level data.
505+ ///
506+ /// **Important:** The string will still be freed when the Zval is dropped.
507+ /// The `persistent` flag only affects which memory allocator is used. If
508+ /// you need a string to outlive the Zval, consider using
509+ /// [`std::mem::forget`] on the Zval or storing the string elsewhere.
510+ ///
511+ /// For most use cases (return values, function arguments, temporary
512+ /// storage), you should use `persistent: false`.
513+ ///
499514 /// # Errors
500515 ///
501516 /// Never returns an error.
@@ -507,6 +522,9 @@ impl Zval {
507522
508523 /// Sets the value of the zval as a Zend string.
509524 ///
525+ /// The Zval takes ownership of the string. When the Zval is dropped,
526+ /// the string will be released.
527+ ///
510528 /// # Parameters
511529 ///
512530 /// * `val` - String content.
@@ -527,9 +545,13 @@ impl Zval {
527545 self . value . str_ = ptr;
528546 }
529547
530- /// Sets the value of the zval as a interned string. Returns nothing in a
548+ /// Sets the value of the zval as an interned string. Returns nothing in a
531549 /// result when successful.
532550 ///
551+ /// Interned strings are stored once and are immutable. PHP stores them in
552+ /// an internal hashtable. Unlike regular strings, interned strings are not
553+ /// reference counted and should not be freed by `zval_ptr_dtor`.
554+ ///
533555 /// # Parameters
534556 ///
535557 /// * `val` - The value to set the zval as.
@@ -540,7 +562,10 @@ impl Zval {
540562 /// Never returns an error.
541563 // TODO: Check if we can drop the result here.
542564 pub fn set_interned_string ( & mut self , val : & str , persistent : bool ) -> Result < ( ) > {
543- self . set_zend_string ( ZendStr :: new_interned ( val, persistent) ) ;
565+ // Use InternedStringEx (without RefCounted) because interned strings
566+ // should not have their refcount modified by zval_ptr_dtor.
567+ self . change_type ( ZvalTypeFlags :: InternedStringEx ) ;
568+ self . value . str_ = ZendStr :: new_interned ( val, persistent) . into_raw ( ) ;
544569 Ok ( ( ) )
545570 }
546571
0 commit comments