@@ -934,6 +934,7 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
934934 return PHP_OUTPUT_HANDLER_FAILURE ;
935935 }
936936
937+ bool still_have_handler = true;
937938 /* storable? */
938939 if (php_output_handler_append (handler , & context -> in ) && !context -> op ) {
939940 context -> op = original_op ;
@@ -948,6 +949,7 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
948949 if (handler -> flags & PHP_OUTPUT_HANDLER_USER ) {
949950 zval ob_args [2 ];
950951 zval retval ;
952+ ZVAL_UNDEF (& retval );
951953
952954 /* ob_data */
953955 ZVAL_STRINGL (& ob_args [0 ], handler -> buffer .data , handler -> buffer .used );
@@ -959,17 +961,48 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
959961 handler -> func .user -> fci .params = ob_args ;
960962 handler -> func .user -> fci .retval = & retval ;
961963
962- #define PHP_OUTPUT_USER_SUCCESS (retval ) ((Z_TYPE(retval) != IS_UNDEF) && !(Z_TYPE(retval) == IS_FALSE))
963- if (SUCCESS == zend_call_function (& handler -> func .user -> fci , & handler -> func .user -> fcc ) && PHP_OUTPUT_USER_SUCCESS (retval )) {
964- /* user handler may have returned TRUE */
965- status = PHP_OUTPUT_HANDLER_NO_DATA ;
966- if (Z_TYPE (retval ) != IS_FALSE && Z_TYPE (retval ) != IS_TRUE ) {
967- convert_to_string (& retval );
968- if (Z_STRLEN (retval )) {
969- context -> out .data = estrndup (Z_STRVAL (retval ), Z_STRLEN (retval ));
970- context -> out .used = Z_STRLEN (retval );
971- context -> out .free = 1 ;
972- status = PHP_OUTPUT_HANDLER_SUCCESS ;
964+ if (SUCCESS == zend_call_function (& handler -> func .user -> fci , & handler -> func .user -> fcc ) && Z_TYPE (retval ) != IS_UNDEF ) {
965+ if (Z_TYPE (retval ) != IS_STRING ) {
966+ // Make sure that we don't get lost in the current output buffer
967+ // by disabling it
968+ handler -> flags |= PHP_OUTPUT_HANDLER_DISABLED ;
969+ php_error_docref (
970+ NULL ,
971+ E_DEPRECATED ,
972+ "Returning a non-string result from user output handler %s is deprecated" ,
973+ ZSTR_VAL (handler -> name )
974+ );
975+ // Check if the handler is still in the list of handlers to
976+ // determine if the PHP_OUTPUT_HANDLER_DISABLED flag can
977+ // be removed
978+ still_have_handler = false;
979+ int handler_count = php_output_get_level ();
980+ if (handler_count ) {
981+ php_output_handler * * handlers = (php_output_handler * * ) zend_stack_base (& OG (handlers ));
982+ for (int handler_num = 0 ; handler_num < handler_count ; ++ handler_num ) {
983+ php_output_handler * curr_handler = handlers [handler_num ];
984+ if (curr_handler == handler ) {
985+ handler -> flags &= (~PHP_OUTPUT_HANDLER_DISABLED );
986+ still_have_handler = true;
987+ break ;
988+ }
989+ }
990+ }
991+ }
992+ if (Z_TYPE (retval ) == IS_FALSE ) {
993+ /* call failed, pass internal buffer along */
994+ status = PHP_OUTPUT_HANDLER_FAILURE ;
995+ } else {
996+ /* user handler may have returned TRUE */
997+ status = PHP_OUTPUT_HANDLER_NO_DATA ;
998+ if (Z_TYPE (retval ) != IS_FALSE && Z_TYPE (retval ) != IS_TRUE ) {
999+ convert_to_string (& retval );
1000+ if (Z_STRLEN (retval )) {
1001+ context -> out .data = estrndup (Z_STRVAL (retval ), Z_STRLEN (retval ));
1002+ context -> out .used = Z_STRLEN (retval );
1003+ context -> out .free = 1 ;
1004+ status = PHP_OUTPUT_HANDLER_SUCCESS ;
1005+ }
9731006 }
9741007 }
9751008 } else {
@@ -996,10 +1029,17 @@ static inline php_output_handler_status_t php_output_handler_op(php_output_handl
9961029 status = PHP_OUTPUT_HANDLER_FAILURE ;
9971030 }
9981031 }
999- handler -> flags |= PHP_OUTPUT_HANDLER_STARTED ;
1032+ if (still_have_handler ) {
1033+ handler -> flags |= PHP_OUTPUT_HANDLER_STARTED ;
1034+ }
10001035 OG (running ) = NULL ;
10011036 }
10021037
1038+ if (!still_have_handler ) {
1039+ // Handler and context will have both already been freed
1040+ return status ;
1041+ }
1042+
10031043 switch (status ) {
10041044 case PHP_OUTPUT_HANDLER_FAILURE :
10051045 /* disable this handler */
@@ -1225,6 +1265,19 @@ static int php_output_stack_pop(int flags)
12251265 }
12261266 php_output_handler_op (orphan , & context );
12271267 }
1268+ // If it isn't still in the stack, cannot free it
1269+ bool still_have_handler = false;
1270+ int handler_count = php_output_get_level ();
1271+ if (handler_count ) {
1272+ php_output_handler * * handlers = (php_output_handler * * ) zend_stack_base (& OG (handlers ));
1273+ for (int handler_num = 0 ; handler_num < handler_count ; ++ handler_num ) {
1274+ php_output_handler * curr_handler = handlers [handler_num ];
1275+ if (curr_handler == orphan ) {
1276+ still_have_handler = true;
1277+ break ;
1278+ }
1279+ }
1280+ }
12281281
12291282 /* pop it off the stack */
12301283 zend_stack_del_top (& OG (handlers ));
@@ -1240,7 +1293,9 @@ static int php_output_stack_pop(int flags)
12401293 }
12411294
12421295 /* destroy the handler (after write!) */
1243- php_output_handler_free (& orphan );
1296+ if (still_have_handler ) {
1297+ php_output_handler_free (& orphan );
1298+ }
12441299 php_output_context_dtor (& context );
12451300
12461301 return 1 ;
0 commit comments