to inject in page. */ global $nunil_csp_meta; $nunil_csp_meta = ''; /** * We'll need to get the number of ob levels we're in, so that * we can iterate over each, collecting that buffer's output * into the final output. */ $levels = ob_get_level(); /** * With output_buffering turned on, there will be one more level managed * by "default output handler" upper in the stack. * Some plugins can open an output buffering level with * ob_start( 'callback') trusting that the callback will be executed to * modify the output before it is sent to the browser. * Those level will be lower in the stack than this mu-plugin's * buffering level. * Callbacks are executed only on ob_flush() and not on ob_get_clean(). * If we capture and destroy these buffering levels with ob_get_clean() * those callbacks will never been called and the output captured and * modified by this mu-plugin could be different from the real output * that would have been sent to the browser with the mu-plugin not in * place. * So we need to flush the levels that are down in the stack untill we * reach our ob_level and then capture its content with * ob_get_contents() to manipulate it. * To catch this mu-plugin's buffering level, we open it with a specific * callback that doesn't do anything, ut allows us to locate it in the * stack pile by its name. * * The for loop loops ob levels from the more internal one (when $i = 0) * to the more external one. * We calculate the $i value of our buffering level and then we flush * all lower levels before capturing and modifying output. * * Not calling ob_get_clean on upper levels will respect the use of such * a mechanics in another eventual mu-plugin that could open a buffer * before this one (that will result in a level upper in the stack than * the one opened by this mu-plugin). */ $key = no_unsafe_inline_search_by_name( 'no_unsafe_inline_mu_plugin_callback' ); if ( $levels >= 0 && $key ) { for ( $i = ( $levels - 1 ); $i >= $key; $i-- ) { if ( $i !== $key && $i >= 0 ) { ob_end_flush(); } else { $final .= (string) ob_get_clean(); } } } $tools = (array) get_option( 'no-unsafe-inline-tools' ); if ( ( isset( $tools['test_policy'] ) && 1 === $tools['test_policy'] ) || ( isset( $tools['enable_protection'] ) && 1 === $tools['enable_protection'] ) || ( isset( $tools['capture_enabled'] ) && 1 === $tools['capture_enabled'] ) ) { /** * Apply any filters to the final output */ $manipulated = apply_filters( 'no_unsafe_inline_final_output', $final ); do_action( 'nunil_output_csp_headers', $manipulated ); /** * Inject meta http-equiv="Content-Security-Policy" if variable is set */ $manipulated = apply_filters( 'no_unsafe_inline_meta_injector', $manipulated ); } else { $manipulated = $final; } // phpcs:ignore echo $manipulated; if ( class_exists( 'Fiber' ) ) { global $nunil_fibers; if ( is_array( $nunil_fibers ) ) { foreach ( $nunil_fibers as $i => $fiber ) { if ( $fiber instanceof Fiber ) { if ( ! $fiber->isStarted() ) { $fiber->start(); } if ( $fiber->isSuspended() ) { $fiber->resume(); } } } } } wp_ob_end_flush_all(); }, 200 // This huge value tries to execute this action after all actions added to the shutdown hook. ); }, 10 ); /** * Callback of the ob_start * * The name is used to detect the buffer level started by the mu-plugin. * * @param string $data String present in the output buffer. * @return string */ function no_unsafe_inline_mu_plugin_callback( $data ) { return $data; } /** * Search for callback name in the 2 dimensional array returned by ob_get_status( true ) * * Returns the array key. * * @param string $name The name of the callback. * @return int */ function no_unsafe_inline_search_by_name( $name ) { $ob_status = ob_get_status( true ); foreach ( $ob_status as $key => $val ) { if ( $val['name'] === $name ) { return intval( $key ); } } return 0; }