@@ -148,14 +148,31 @@ static php_stream_filter_status_t userfilter_filter(
148148 uint32_t orig_no_fclose = stream -> flags & PHP_STREAM_FLAG_NO_FCLOSE ;
149149 stream -> flags |= PHP_STREAM_FLAG_NO_FCLOSE ;
150150
151- zval * stream_prop = zend_hash_str_find_ind (Z_OBJPROP_P (obj ), "stream" , sizeof ("stream" )- 1 );
152- if (stream_prop ) {
153- /* Give the userfilter class a hook back to the stream */
154- zval_ptr_dtor (stream_prop );
155- php_stream_to_zval (stream , stream_prop );
156- Z_ADDREF_P (stream_prop );
151+ /* Give the userfilter class a hook back to the stream */
152+ zend_class_entry * old_scope = EG (fake_scope );
153+ EG (fake_scope ) = Z_OBJCE_P (obj );
154+
155+ zend_string * stream_name = ZSTR_INIT_LITERAL ("stream" , 0 );
156+ bool stream_property_exists = Z_OBJ_HT_P (obj )-> has_property (Z_OBJ_P (obj ), stream_name , ZEND_PROPERTY_EXISTS , NULL );
157+ if (stream_property_exists ) {
158+ zval stream_zval ;
159+ php_stream_to_zval (stream , & stream_zval );
160+ zend_update_property_ex (Z_OBJCE_P (obj ), Z_OBJ_P (obj ), stream_name , & stream_zval );
161+ /* If property update threw an exception, skip filter execution */
162+ if (EG (exception )) {
163+ EG (fake_scope ) = old_scope ;
164+ if (buckets_in -> head ) {
165+ php_error_docref (NULL , E_WARNING , "Unprocessed filter buckets remaining on input brigade" );
166+ }
167+ zend_string_release (stream_name );
168+ stream -> flags &= ~PHP_STREAM_FLAG_NO_FCLOSE ;
169+ stream -> flags |= orig_no_fclose ;
170+ return PSFS_ERR_FATAL ;
171+ }
157172 }
158173
174+ EG (fake_scope ) = old_scope ;
175+
159176 ZVAL_STRINGL (& func_name , "filter" , sizeof ("filter" )- 1 );
160177
161178 /* Setup calling arguments */
@@ -196,11 +213,16 @@ static php_stream_filter_status_t userfilter_filter(
196213
197214 /* filter resources are cleaned up by the stream destructor,
198215 * keeping a reference to the stream resource here would prevent it
199- * from being destroyed properly */
200- if (stream_prop ) {
201- convert_to_null (stream_prop );
216+ * from being destroyed properly.
217+ * Since the property accepted a resource assignment above, it must have
218+ * no type hint or be typed as mixed, so we can safely assign null.
219+ */
220+ if (stream_property_exists ) {
221+ zend_update_property_null (Z_OBJCE_P (obj ), Z_OBJ_P (obj ), ZSTR_VAL (stream_name ), ZSTR_LEN (stream_name ));
202222 }
203223
224+ zend_string_release (stream_name );
225+
204226 zval_ptr_dtor (& args [3 ]);
205227 zval_ptr_dtor (& args [2 ]);
206228 zval_ptr_dtor (& args [1 ]);
0 commit comments