%PDF- %PDF-
Direktori : /var/www/pn/wp-content/plugins/wp-rocket/inc/Engine/CriticalPath/ |
Current File : /var/www/pn/wp-content/plugins/wp-rocket/inc/Engine/CriticalPath/CriticalCSSSubscriber.php |
<?php namespace WP_Rocket\Engine\CriticalPath; use WP_Rocket\Admin\Options_Data; use WP_Rocket\Event_Management\Subscriber_Interface; use WP_Filesystem_Direct; /** * Critical CSS Subscriber. * * @since 3.3 */ class CriticalCSSSubscriber implements Subscriber_Interface { /** * Instance of Critical CSS. * * @var Critical_CSS */ protected $critical_css; /** * Instance of options. * * @var Options_Data */ protected $options; /** * Instance of the filesystem handler. * * @var WP_Filesystem_Direct */ private $filesystem; /** * CPCSS generation and deletion service. * * @var ProcessorService instance for this service. */ private $cpcss_service; /** * Creates an instance of the Critical CSS Subscriber. * * @param CriticalCSS $critical_css Critical CSS instance. * @param ProcessorService $cpcss_service Has the logic for cpcss generation and deletion. * @param Options_Data $options WP Rocket options. * @param WP_Filesystem_Direct $filesystem Instance of the filesystem handler. */ public function __construct( CriticalCSS $critical_css, ProcessorService $cpcss_service, Options_Data $options, $filesystem ) { $this->critical_css = $critical_css; $this->cpcss_service = $cpcss_service; $this->options = $options; $this->filesystem = $filesystem; } /** * Return an array of events that this subscriber wants to listen to. * * @since 3.3 * * @return array */ public static function get_subscribed_events() { // phpcs:disable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned return [ 'admin_post_rocket_generate_critical_css' => 'init_critical_css_generation', 'update_option_' . rocket_get_constant( 'WP_ROCKET_SLUG' ) => [ [ 'generate_critical_css_on_activation', 11, 2 ], [ 'stop_process_on_deactivation', 11, 2 ], [ 'maybe_generate_cpcss_mobile', 12, 2 ], ], 'admin_notices' => [ [ 'notice_critical_css_generation_triggered' ], [ 'critical_css_generation_running_notice' ], [ 'critical_css_generation_complete_notice' ], [ 'warning_critical_css_dir_permissions' ], ], 'wp_head' => [ 'insert_load_css', PHP_INT_MAX ], 'rocket_buffer' => [ [ 'insert_critical_css_buffer', 19 ], [ 'async_css', 32 ], ], 'switch_theme' => 'maybe_regenerate_cpcss', 'rocket_excluded_inline_js_content' => 'exclude_inline_js', 'before_delete_post' => 'delete_cpcss', 'admin_post_rocket_rollback' => [ 'stop_critical_css_generation', 9 ], 'wp_rocket_upgrade' => [ 'stop_critical_css_generation', 9 ], ]; // phpcs:enable WordPress.Arrays.MultipleStatementAlignment.DoubleArrowNotAligned } /** * Deletes the custom CPCSS files from /posts/ folder. * * @since 3.6 * * @param int $post_id Deleted post id. */ public function delete_cpcss( $post_id ) { if ( ! current_user_can( 'rocket_regenerate_critical_css' ) ) { return; } if ( ! $this->options->get( 'async_css', 0 ) ) { return; } $post_type = get_post_type( $post_id ); $item_path = 'posts' . DIRECTORY_SEPARATOR . "{$post_type}-{$post_id}.css"; $this->cpcss_service->process_delete( $item_path ); if ( $this->options->get( 'async_css_mobile', 0 ) ) { $mobile_item_path = 'posts' . DIRECTORY_SEPARATOR . "{$post_type}-{$post_id}-mobile.css"; $this->cpcss_service->process_delete( $mobile_item_path ); } } /** * This notice is displayed when the Critical CSS Generation is triggered from a different page than * WP Rocket settings page. * * @since 3.4.1 */ public function notice_critical_css_generation_triggered() { if ( ! current_user_can( 'rocket_regenerate_critical_css' ) ) { return; } $screen = get_current_screen(); if ( 'settings_page_wprocket' === $screen->id ) { return; } if ( false === get_transient( 'rocket_critical_css_generation_triggered' ) ) { return; } delete_transient( 'rocket_critical_css_generation_triggered' ); $message = __( 'Critical CSS generation is currently running.', 'rocket' ); if ( current_user_can( 'rocket_manage_options' ) ) { $message .= ' ' . sprintf( // Translators: %1$s = opening link tag, %2$s = closing link tag. __( 'Go to the %1$sWP Rocket settings%2$s page to track progress.', 'rocket' ), '<a href="' . esc_url( admin_url( 'options-general.php?page=' . WP_ROCKET_PLUGIN_SLUG ) ) . '">', '</a>' ); } rocket_notice_html( [ 'status' => 'info', 'message' => $message, ] ); } /** * Launches the critical CSS generation from admin. * * @since 2.11 * * @see CriticalCSS::process_handler() */ public function init_critical_css_generation() { if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'rocket_generate_critical_css' ) ) { wp_nonce_ays( '' ); } if ( ! current_user_can( 'rocket_regenerate_critical_css' ) ) { wp_die(); } $version = 'default'; if ( $this->critical_css->is_async_css_mobile() ) { $version = 'all'; } $this->critical_css->process_handler( $version ); if ( ! strpos( wp_get_referer(), 'wprocket' ) ) { set_transient( 'rocket_critical_css_generation_triggered', 1 ); } wp_safe_redirect( esc_url_raw( wp_get_referer() ) ); rocket_get_constant( 'WP_ROCKET_IS_TESTING', false ) ? wp_die() : exit; } /** * Launches the critical CSS generation when activating the async CSS option. * * @since 2.11 * * @param array $old_value Previous values for WP Rocket settings. * @param array $value New values for WP Rocket settings. * * @see CriticalCSS::process_handler() */ public function generate_critical_css_on_activation( $old_value, $value ) { if ( ! isset( $old_value['async_css'], $value['async_css'] ) || ( $old_value['async_css'] === $value['async_css'] ) || 1 !== (int) $value['async_css'] ) { return; } $critical_css_path = $this->critical_css->get_critical_css_path(); // Check if the CPCSS path exists and create it. if ( ! $this->filesystem->is_dir( $critical_css_path ) ) { rocket_mkdir_p( $critical_css_path ); } $version = 'default'; if ( isset( $value['do_caching_mobile_files'], $value['async_css_mobile'] ) && ( 1 === (int) $value['do_caching_mobile_files'] && 1 === (int) $value['async_css_mobile'] ) ) { $version = 'all'; } // Generate the CPCSS files. $this->critical_css->process_handler( $version ); } /** * Maybe generate the CPCSS for Mobile. * * @since 3.6 * * @param array $old_value Array of original values. * @param array $value Array of new values. */ public function maybe_generate_cpcss_mobile( $old_value, $value ) { if ( ! isset( $value['async_css_mobile'] ) || 1 !== (int) $value['async_css_mobile'] ) { return; } if ( ! isset( $value['do_caching_mobile_files'] ) || 1 !== (int) $value['do_caching_mobile_files'] ) { return; } if ( ! isset( $old_value['async_css'], $value['async_css'] ) || ( ( $old_value['async_css'] !== $value['async_css'] ) && 1 === (int) $value['async_css'] ) || 1 !== (int) $value['async_css'] ) { return; } $this->critical_css->process_handler( 'mobile' ); } /** * Stops the critical CSS generation when deactivating the async CSS option and remove the notices. * * @since 2.11 * * @param array $old_value Previous values for WP Rocket settings. * @param array $value New values for WP Rocket settings. */ public function stop_process_on_deactivation( $old_value, $value ) { if ( ! empty( $_POST[ WP_ROCKET_SLUG ] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing && isset( $old_value['async_css'], $value['async_css'] ) && ( $old_value['async_css'] !== $value['async_css'] ) && 0 === (int) $value['async_css'] ) { $this->stop_critical_css_generation(); } } /** * This notice is displayed when the critical CSS generation is running. * * @since 2.11 */ public function critical_css_generation_running_notice() { if ( ! current_user_can( 'rocket_regenerate_critical_css' ) ) { return; } $screen = get_current_screen(); if ( 'settings_page_wprocket' !== $screen->id ) { return; } $transient = get_transient( 'rocket_critical_css_generation_process_running' ); if ( ! $transient ) { return; } $success_counter = 0; $items_message = ''; if ( ! empty( $transient['items'] ) ) { $items_message .= '<ul>'; foreach ( $transient['items'] as $item ) { $status_nonmobile = isset( $item['status']['nonmobile'] ); $status_mobile = $this->is_mobile_cpcss_active() ? isset( $item['status']['mobile'] ) : true; if ( $status_nonmobile && $status_mobile ) { $items_message .= '<li>' . $item['status']['nonmobile']['message'] . '</li>'; if ( $item['status']['nonmobile']['success'] ) { $success_counter ++; } } } $items_message .= '</ul>'; } if ( ! isset( $transient['total'] ) ) { return; } if ( 0 === $success_counter && 0 === $transient['total'] ) { return; } $message = '<p>' . sprintf( // Translators: %1$d = number of critical CSS generated, %2$d = total number of critical CSS to generate. __( 'Critical CSS generation is currently running: %1$d of %2$d page types completed. (Refresh this page to view progress)', 'rocket' ), $success_counter, $transient['total'] ) . '</p>' . $items_message; rocket_notice_html( [ 'status' => 'info', 'message' => $message, ] ); } /** * This notice is displayed when the critical CSS generation is complete. * * @since 2.11 */ public function critical_css_generation_complete_notice() { if ( ! current_user_can( 'rocket_regenerate_critical_css' ) ) { return; } $screen = get_current_screen(); if ( 'settings_page_wprocket' !== $screen->id ) { return; } $transient = get_transient( 'rocket_critical_css_generation_process_complete' ); if ( ! $transient ) { return; } $status = 'success'; $success_counter = 0; $items_message = ''; $desktop = false; if ( ! empty( $transient['items'] ) ) { $items_message .= '<ul>'; foreach ( $transient['items'] as $item ) { $status_nonmobile = isset( $item['status']['nonmobile'] ); $status_mobile = $this->is_mobile_cpcss_active() ? isset( $item['status']['mobile'] ) : true; if ( ! $status_nonmobile || ! $status_mobile ) { continue; } if ( isset( $item['status']['nonmobile']['message'] ) ) { $desktop = true; } $items_message .= '<li>' . $item['status']['nonmobile']['message'] . '</li>'; if ( $item['status']['nonmobile']['success'] ) { $success_counter ++; } } $items_message .= '</ul>'; } if ( ! $desktop || ( 0 === $success_counter && 0 === $transient['total'] ) ) { return; } if ( 0 === $success_counter ) { $status = 'error'; } elseif ( $success_counter < $transient['total'] ) { $status = 'warning'; } $message = '<p>' . sprintf( // Translators: %1$d = number of critical CSS generated, %2$d = total number of critical CSS to generate. __( 'Critical CSS generation finished for %1$d of %2$d page types.', 'rocket' ), $success_counter, $transient['total'] ); $message .= ' <em> (' . date_i18n( get_option( 'date_format' ) ) . ' @ ' . date_i18n( get_option( 'time_format' ) ) . ') </em></p>' . $items_message; if ( 'error' === $status || 'warning' === $status ) { $message .= '<p>' . __( 'Critical CSS generation encountered one or more errors.', 'rocket' ) . ' <a href="https://docs.wp-rocket.me/article/1267-troubleshooting-critical-css-generation-issues" data-beacon-article="5d5214d10428631e94f94ae6" target="_blank" rel="noreferer noopener">' . __( 'Learn more.', 'rocket' ) . '</a>'; } rocket_notice_html( [ 'status' => $status, 'message' => $message, ] ); delete_transient( 'rocket_critical_css_generation_process_complete' ); } /** * This warning is displayed when the critical CSS dir isn't writeable. * * @since 2.11 */ public function warning_critical_css_dir_permissions() { if ( current_user_can( 'rocket_manage_options' ) && ( ! $this->filesystem->is_writable( WP_ROCKET_CRITICAL_CSS_PATH ) ) && ( $this->options->get( 'async_css', false ) ) && rocket_valid_key() ) { $boxes = get_user_meta( get_current_user_id(), 'rocket_boxes', true ); if ( in_array( __FUNCTION__, (array) $boxes, true ) ) { return; } $message = rocket_notice_writing_permissions( trim( str_replace( ABSPATH, '', WP_ROCKET_CRITICAL_CSS_PATH ), '/' ) ); rocket_notice_html( [ 'status' => 'error', 'dismissible' => '', 'message' => $message, ] ); } } /** * Insert loadCSS script in <head>. * * @since 2.11.2 Updated loadCSS rel=preload polyfill to version 2.0.1 * @since 2.10 */ public function insert_load_css() { if ( ! $this->should_async_css() ) { return; } // This filter is documented in inc/classes/Buffer/class-tests.php. $rocket_cache_search = apply_filters( 'rocket_cache_search', false ); // Don't apply on search page. if ( is_search() && ! $rocket_cache_search ) { return; } // Don't apply on 404 page. if ( is_404() ) { return; } if ( empty( $this->critical_css->get_current_page_critical_css() ) && empty( $this->options->get( 'critical_css', '' ) ) ) { return; } echo /* phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Dynamic content is properly escaped in the view. */ <<<JS <script> /*! loadCSS rel=preload polyfill. [c]2017 Filament Group, Inc. MIT License */ (function(w){"use strict";if(!w.loadCSS){w.loadCSS=function(){}} var rp=loadCSS.relpreload={};rp.support=(function(){var ret;try{ret=w.document.createElement("link").relList.supports("preload")}catch(e){ret=!1} return function(){return ret}})();rp.bindMediaToggle=function(link){var finalMedia=link.media||"all";function enableStylesheet(){link.media=finalMedia} if(link.addEventListener){link.addEventListener("load",enableStylesheet)}else if(link.attachEvent){link.attachEvent("onload",enableStylesheet)} setTimeout(function(){link.rel="stylesheet";link.media="only x"});setTimeout(enableStylesheet,3000)};rp.poly=function(){if(rp.support()){return} var links=w.document.getElementsByTagName("link");for(var i=0;i<links.length;i++){var link=links[i];if(link.rel==="preload"&&link.getAttribute("as")==="style"&&!link.getAttribute("data-loadcss")){link.setAttribute("data-loadcss",!0);rp.bindMediaToggle(link)}}};if(!rp.support()){rp.poly();var run=w.setInterval(rp.poly,500);if(w.addEventListener){w.addEventListener("load",function(){rp.poly();w.clearInterval(run)})}else if(w.attachEvent){w.attachEvent("onload",function(){rp.poly();w.clearInterval(run)})}} if(typeof exports!=="undefined"){exports.loadCSS=loadCSS} else{w.loadCSS=loadCSS}}(typeof global!=="undefined"?global:this)) </script> JS; } /** * Insert critical CSS before combined CSS when option is active. * * @since 2.11.5 * * @param string $buffer HTML output of the page. * * @return string Updated HTML output */ public function insert_critical_css_buffer( $buffer ) { if ( ! $this->should_async_css() ) { return $buffer; } $critical_css_content = $this->critical_css->get_critical_css_content(); if ( empty( $critical_css_content ) ) { return $buffer; } $critical_css_content = str_replace( '\\', '\\\\', $critical_css_content ); $buffer = preg_replace( '#</title>#iU', '</title><style id="rocket-critical-css">' . wp_strip_all_tags( $critical_css_content ) . '</style>', $buffer, 1 ); return preg_replace( '#</body>#iU', $this->return_remove_cpcss_script() . '</body>', $buffer, 1 ); } /** * Returns JS script to remove the critical css style from frontend. * * @since 3.6 * * @return string */ protected function return_remove_cpcss_script() { $filename = rocket_get_constant( 'SCRIPT_DEBUG' ) ? 'cpcss-removal.js' : 'cpcss-removal.min.js'; $script = rocket_get_constant( 'WP_ROCKET_PATH' ) . "assets/js/{$filename}"; if ( ! is_readable( $script ) ) { return ''; } return sprintf( '<script>%s</script>', $this->filesystem->get_contents( $script ) ); } /** * Adds wprRemoveCPCSS to excluded inline JS array. * * @since 3.6 * * @param array $excluded_inline Array of inline JS excluded from being combined. * * @return array */ public function exclude_inline_js( array $excluded_inline ) { $excluded_inline[] = 'wprRemoveCPCSS'; return $excluded_inline; } /** * Defer loading of CSS files. * * @since 2.10 * * @param string $buffer HTML code. * * @return string Updated HTML code */ public function async_css( $buffer ) { if ( ! $this->should_async_css() ) { return $buffer; } if ( empty( $this->critical_css->get_current_page_critical_css() ) && empty( $this->options->get( 'critical_css', '' ) ) ) { return $buffer; } $excluded_css = array_flip( $this->critical_css->get_exclude_async_css() ); /** * Filters the pattern used to get all stylesheets in the HTML. * * @since 2.10 * * @param string $css_pattern Regex pattern to get all stylesheets in the HTML. */ $css_pattern = apply_filters( 'rocket_async_css_regex_pattern', '/(?=<link[^>]*\s(rel\s*=\s*[\'"]stylesheet["\']))<link[^>]*\shref\s*=\s*[\'"]([^\'"]+)[\'"](.*)>/iU' ); // Get all css files with this regex. preg_match_all( $css_pattern, $buffer, $tags_match ); if ( ! isset( $tags_match[0] ) ) { return $buffer; } $noscripts = '<noscript>'; foreach ( $tags_match[0] as $i => $tag ) { // Strip query args. $path = wp_parse_url( $tags_match[2][ $i ], PHP_URL_PATH ); // Check if this file should be deferred. if ( isset( $excluded_css[ $path ] ) ) { continue; } if ( false !== strpos( $tags_match[0][ $i ], 'media="print"' ) ) { continue; } $preload = str_replace( 'stylesheet', 'preload', $tags_match[1][ $i ] ); $onload = preg_replace( '~' . preg_quote( $tags_match[3][ $i ], '~' ) . '~iU', ' data-rocket-async="style" as="style" onload=""' . $tags_match[3][ $i ] . '>', $tags_match[3][ $i ] ); $tag = str_replace( $tags_match[3][ $i ] . '>', $onload, $tag ); $tag = str_replace( $tags_match[1][ $i ], $preload, $tag ); $tag = str_replace( 'onload=""', 'onload="this.onload=null;this.rel=\'stylesheet\'"', $tag ); $tag = preg_replace( '/(id\s*=\s*[\"\'](?:[^\"\']*)*[\"\'])/i', '', $tag ); $buffer = str_replace( $tags_match[0][ $i ], $tag, $buffer ); $noscripts .= $tags_match[0][ $i ]; } $noscripts .= '</noscript>'; return str_replace( '</body>', $noscripts . '</body>', $buffer ); } /** * Regenerates the CPCSS when switching theme if the option is active. * * @since 3.3 */ public function maybe_regenerate_cpcss() { if ( ! $this->options->get( 'async_css' ) ) { return; } $this->critical_css->process_handler(); } /** * Checks if mobile CPCSS is active. * * @since 3.6 * * @return boolean CPCSS active or not. */ private function is_mobile_cpcss_active() { return ( $this->options->get( 'async_css', 0 ) && $this->options->get( 'cache_mobile', 0 ) && $this->options->get( 'do_caching_mobile_files', 0 ) ) && $this->options->get( 'async_css_mobile', 0 ); } /** * Checks if we should async CSS * * @since 3.6.2.1 * * @return boolean */ private function should_async_css() { if ( rocket_get_constant( 'DONOTROCKETOPTIMIZE' ) ) { return false; } if ( ! $this->options->get( 'async_css', 0 ) ) { return false; } return ! is_rocket_post_excluded_option( 'async_css' ); } /** * Stops the critical CSS generation. * * @since 3.10 * * @return void */ public function stop_critical_css_generation() { $this->critical_css->stop_generation(); delete_transient( 'rocket_critical_css_generation_process_running' ); delete_transient( 'rocket_critical_css_generation_process_complete' ); } }