PluginProbe
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization / trunk
Optimole – Optimize Images | Convert WebP & AVIF | CDN & Lazy Load | Image Optimization vtrunk
4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 2.5.5 2.5.6 2.5.7 3.0.0 3.0.1 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.11.0 3.11.1 3.11.2 3.11.3 3.12.0 All 135 releases
← All changes | inc/manager.php +63 -32 4.2.12 → trunk View file →
@@ -115,8 +115,9 @@
115 115 'speedycache',
116 116 'hummingbird',
117 117 'aruba_hsc',
118 118 'spc',
119 + 'groovy_menu',
119 120 ];
120 121 /**
121 122 * The current state of the buffer.
122 123 *
@@ -392,9 +393,9 @@
392 393 }
393 394 if ( ! wp_doing_ajax() ) {
394 395 return false;
395 396 }
396 - if ( isset( $_REQUEST['action'] ) && strpos( $_REQUEST['action'], 'wpmdb' ) !== false ) {
397 + if ( isset( $_REQUEST['action'] ) && is_string( $_REQUEST['action'] ) && strpos( $_REQUEST['action'], 'wpmdb' ) !== false ) {
397 398 return false;
398 399 }
399 400
400 401 return true;
@@ -911,22 +912,25 @@
911 912 $this->start_capture_buffer();
912 913 }
913 914
914 915 /**
915 - * Start an output buffer that captures the page HTML.
916 + * Start the output buffer that holds the page HTML.
916 917 *
917 - * On normal requests the buffer is captured and processed by close_buffer()
918 - * at shutdown, outside of PHP's display-handler context, so callbacks hooked
919 - * into our filters are free to use output buffering themselves and fatal
920 - * errors raised during processing keep their real message instead of being
921 - * masked by "Cannot use output buffering in output buffering display handlers".
918 + * By default the page is processed by the attached handler when the buffer
919 + * is flushed, the way it worked up to 4.2.11. We never flush other buffers
920 + * and open no buffer at shutdown, so code that opens a buffer early and reads
921 + * it back with ob_get_clean() at shutdown (FacetWP, Groovy Menu) keeps working.
922 922 *
923 - * The attached handler is only a fallback for buffers flushed outside of
924 - * close_buffer() — third-party force-flush loops, ob_flush() streaming, or
925 - * core's wp_ob_end_flush_all() reaching the re-armed buffer. A named method
926 - * is used instead of a closure so the buffer can be identified as ours via
927 - * ob_get_status()['name'].
923 + * When the optml_capture_at_shutdown filter returns true, close_buffer()
924 + * captures and processes the buffer at shutdown instead, outside of PHP's
925 + * display-handler context. Callbacks hooked into our filters can then use
926 + * output buffering themselves, and fatal errors raised during processing
927 + * keep their real message instead of being masked by "Cannot use output
928 + * buffering in output buffering display handlers".
928 929 *
930 + * A named method is used instead of a closure so the buffer can be
931 + * identified as ours via ob_get_status()['name'].
932 + *
929 933 * @return void
930 934 */
931 935 private function start_capture_buffer() {
932 936 self::$ob_processed = false;
@@ -939,18 +943,38 @@
939 943 */
940 944 const OB_HANDLER_NAME = 'Optml_Manager::handle_buffer_fallback';
941 945
942 946 /**
943 - * Output-buffer handler attached to our capture buffer.
947 + * Whether the page is captured and processed at shutdown, outside of PHP's display-handler context.
944 948 *
945 - * Runs only when the buffer is flushed outside of close_buffer(). Content is
946 - * passed through UNPROCESSED here: running the replacement filter graph
947 - * inside a PHP display handler would turn any third-party ob_*() call into
948 - * an uncatchable fatal ("Cannot use output buffering in output buffering
949 - * display handlers") — the very crash this rework removes. The only
950 - * exception is the legacy mode selected via the optml_capture_at_shutdown
951 - * filter, which explicitly restores the previous in-handler processing.
949 + * @return bool
950 + */
951 + private function captures_at_shutdown() {
952 + /**
953 + * Filters whether the page is captured and processed at shutdown, outside
954 + * of PHP's display-handler context, instead of inside the output-buffer
955 + * handler.
956 + *
957 + * Off by default: the shutdown capture flushes the buffers
958 + * stacked above ours and opens a new buffer afterwards, which breaks code
959 + * that reads its own buffer back at shutdown. Return true to opt in.
960 + *
961 + * @param bool $capture_at_shutdown Whether to process the buffer at shutdown.
962 + */
963 + return apply_filters( 'optml_capture_at_shutdown', false ) === true;
964 + }
965 +
966 + /**
967 + * Output-buffer handler attached to our buffer.
952 968 *
969 + * By default this is where the page is processed. An exception thrown by the
970 + * replacement never breaks the page: the content is returned untouched.
971 + *
972 + * With the optml_capture_at_shutdown opt-in the handler runs only when the
973 + * buffer is flushed outside of close_buffer(), and the content is passed
974 + * through unprocessed, because running the replacement filter graph inside a
975 + * display handler is what that mode exists to avoid.
976 + *
953 977 * @param string $content The buffered content.
954 978 * @param int $phase PHP's output-handler phase bitmask (unused; keeps replace_content()'s $partial parameter shielded from it).
955 979 *
956 980 * @return string The content to output.
@@ -958,9 +982,9 @@
958 982 public function handle_buffer_fallback( $content, $phase = 0 ) {
959 983 if ( self::$ob_processed || $content === '' ) {
960 984 return $content;
961 985 }
962 - if ( apply_filters( 'optml_capture_at_shutdown', true ) === false ) {
986 + if ( ! $this->captures_at_shutdown() ) {
963 987 try {
964 988 return $this->replace_content( $content, self::is_ajax_request() );
965 989 } catch ( Throwable $t ) {
966 990 // Never break the page from inside a display handler.
@@ -980,16 +1004,9 @@
980 1004 if ( ! self::$ob_started ) {
981 1005 return;
982 1006 }
983 1007
984 - /**
985 - * Filters whether the captured page is processed at shutdown, outside of
986 - * PHP's display-handler context. Return false to restore the legacy
987 - * behavior of processing inside the output-buffer handler.
988 - *
989 - * @param bool $capture_at_shutdown Whether to process the buffer at shutdown.
990 - */
991 - if ( apply_filters( 'optml_capture_at_shutdown', true ) === false ) {
1008 + if ( ! $this->captures_at_shutdown() ) {
992 1009 if ( ob_get_length() ) {
993 1010 ob_end_flush();
994 1011 }
995 1012 return;
@@ -1025,12 +1042,12 @@
1025 1042 *
1026 1043 * @return void
1027 1044 */
1028 1045 public function close_final_buffer() {
1029 - if ( ! self::$ob_started ) {
1046 + if ( ! self::$ob_started || ! $this->captures_at_shutdown() ) {
1030 1047 return;
1031 1048 }
1032 - $this->capture_and_process_buffer();
1049 + $this->capture_and_process_buffer( false );
1033 1050 }
1034 1051
1035 1052 /**
1036 1053 * Capture our buffer, process it outside the display-handler context and echo the result.
@@ -1038,11 +1055,13 @@
1038 1055 * Ownership is verified by both nesting level and handler identity, so a
1039 1056 * buffer another plugin opened at the same level after ours was closed is
1040 1057 * never captured or closed by us.
1041 1058 *
1059 + * @param bool $is_page Whether this is the page capture (true) or the late shutdown output (false).
1060 + *
1042 1061 * @return bool Whether our buffer was found and consumed.
1043 1062 */
1044 - private function capture_and_process_buffer() {
1063 + private function capture_and_process_buffer( $is_page = true ) {
1045 1064 if ( self::$ob_level === 0 || ob_get_level() !== self::$ob_level ) {
1046 1065 return false;
1047 1066 }
1048 1067 $status = ob_get_status();
@@ -1053,8 +1072,20 @@
1053 1072 // Set before ob_end_clean() so our handler no-ops during buffer cleanup.
1054 1073 self::$ob_processed = true;
1055 1074 ob_end_clean();
1056 1075 if ( $html !== false && $html !== '' ) {
1076 + if ( $is_page ) {
1077 + /**
1078 + * Filters the captured page HTML before Optimole processes it.
1079 + *
1080 + * Runs once per request, on the buffer captured at shutdown, outside of
1081 + * PHP's display-handler context. Late output echoed by other shutdown
1082 + * callbacks is not passed through this filter.
1083 + *
1084 + * @param string $html The full page HTML.
1085 + */
1086 + $html = apply_filters( 'optml_captured_page_html', $html );
1087 + }
1057 1088 echo $this->replace_content( $html, self::is_ajax_request() ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- full page HTML, escaping would break the page.
1058 1089 }
1059 1090 return true;
1060 1091 }