nks for the fonts in the used CSS * * @param string $used_css Used CSS content. * * @return void */ private function add_used_fonts_preload( string $used_css ): void { /** * Filters the fonts preload from the used CSS * * @since 3.11 * * @param bool $enable True to enable, false to disable. */ if ( ! apply_filters( 'rocket_enable_rucss_fonts_preload', false ) ) { return; } if ( ! preg_match_all( '/@font-face\s*{\s*(?[^}]+)}/is', $used_css, $font_faces, PREG_SET_ORDER ) ) { return; } if ( empty( $font_faces ) ) { return; } /** * Filters the list of fonts to exclude from preload * * @since 3.15.10 * * @param array $excluded_fonts_preload List of fonts to exclude from preload */ $exclude_fonts_preload = wpm_apply_filters_typed( 'array', 'rocket_exclude_rucss_fonts_preload', [] ); $urls = []; foreach ( $font_faces as $font_face ) { if ( empty( $font_face['content'] ) ) { continue; } $font_url = $this->extract_first_font( $font_face['content'] ); /** * Filters font URL with CDN hostname * * @since 3.11.4 * * @param string $url url to be rewritten. */ $font_url = apply_filters( 'rocket_font_url', $font_url ); if ( empty( $font_url ) ) { continue; } // Making sure the excluded fonts array isn't empty to avoid excluding all fonts. if ( ! empty( $exclude_fonts_preload ) ) { $exclude_fonts_preload = array_filter( $exclude_fonts_preload ); // Combine the array elements into a single string with | as a separator and returning a pattern. $exclude_fonts_preload_pattern = implode( '|', array_map( function ( $item ) { return is_string( $item ) ? preg_quote( $item, '/' ) : ''; }, $exclude_fonts_preload ) ); // Check if the font URL matches any part of the exclude_fonts_preload array. if ( ! empty( $exclude_fonts_preload_pattern ) && preg_match( '/' . $exclude_fonts_preload_pattern . '/i', $font_url ) ) { continue; // Skip this iteration as the font URL is in the exclusion list. } } $urls[] = $font_url; } if ( empty( $urls ) ) { return; } $this->preloaded_fonts = array_unique( $urls ); } /** * Remove preconnect tag for google api. * * @param string $html html content. * * @return string */ protected function remove_google_font_preconnect( string $html ): string { $clean_html = $this->hide_comments( $html ); $clean_html = $this->hide_noscripts( $clean_html ); $clean_html = $this->hide_scripts( $clean_html ); $links = $this->find( ']+[\s"\'])?rel\s*=\s*[\'"]((preconnect)|(dns-prefetch))[\'"]([^>]+)?\/?>', $clean_html, 'Uis' ); foreach ( $links as $link ) { if ( preg_match( '/href=[\'"](https:)?\/\/fonts.googleapis.com\/?[\'"]/', $link[0] ) ) { $html = str_replace( $link[0], '', $html ); } } return $html; } /** * Extracts the first font URL from the font-face declaration * * Skips .eot fonts if it exists * * @since 3.11 * * @param string $font_face Font-face declaration content. * * @return string */ private function extract_first_font( string $font_face ): string { if ( ! preg_match_all( '/src:\s*(?[^;}]*)/is', $font_face, $sources, PREG_SET_ORDER ) ) { return ''; } foreach ( $sources as $src ) { if ( empty( $src['urls'] ) ) { continue; } $urls = explode( ',', $src['urls'] ); foreach ( $urls as $url ) { if ( false !== strpos( $url, '.eot' ) ) { continue; } if ( ! preg_match( '/url\(\s*[\'"]?(?[^\'")]+)[\'"]?\)/is', $url, $matches ) ) { continue; } return trim( $matches['url'] ); } } return ''; } /** * Set Rucss inline attr exclusions * * @return void */ private function set_inline_exclusions_lists() { $wpr_dynamic_lists = $this->data_manager->get_lists(); $this->inline_atts_exclusions = isset( $wpr_dynamic_lists->rucss_inline_atts_exclusions ) ? $wpr_dynamic_lists->rucss_inline_atts_exclusions : []; $this->inline_content_exclusions = isset( $wpr_dynamic_lists->rucss_inline_content_exclusions ) ? $wpr_dynamic_lists->rucss_inline_content_exclusions : []; } /** * Displays a notice if the used CSS folder is not writable * * @since 3.11.4 * * @return void */ public function notice_write_permissions() { if ( ! current_user_can( 'rocket_manage_options' ) ) { return; } if ( ! $this->is_enabled() ) { return; } if ( $this->filesystem->is_writable_folder() ) { return; } $message = rocket_notice_writing_permissions( trim( str_replace( rocket_get_constant( 'ABSPATH', '' ), '', rocket_get_constant( 'WP_ROCKET_USED_CSS_PATH', '' ) ), '/' ) ); rocket_notice_html( [ 'status' => 'error', 'dismissible' => '', 'message' => $message, ] ); } /** * Validate the items in array to be strings only and preg_quote them. * * @param array $items Array to be validated and quoted. * * @return array|string[] */ private function validate_array_and_quote( array $items ) { $items_array = array_filter( $items, 'is_string' ); return array_map( static function ( $item ) { return preg_quote( $item, '/' ); }, $items_array ); } /** * Check if database has at least one completed row. * * @return bool */ public function has_one_completed_row_at_least() { return $this->used_css_query->get_completed_count() > 0; } /** * Get generated used CSS, getter method for used_css_content property. * * @return string */ public function get_used_css_content() { if ( ! $this->context->is_allowed() ) { return ''; } return $this->used_css_content; } }