uawdijnntqw1x1x1
IP : 3.148.196.9
Hostname : premium160.web-hosting.com
Kernel : Linux premium160.web-hosting.com 4.18.0-553.lve.el8.x86_64 #1 SMP Mon May 27 15:27:34 UTC 2024 x86_64
Disable Function : None :)
OS : Linux
PATH:
/
home
/
batcwwjx
/
www
/
wp-content
/
plugins
/
stackable-ultimate-gutenberg-blocks
/
.
/
src
/
css-optimize.php
/
/
<?php /** * CSS Optimization: Optimizes the CSS rendered by our blocks in the frontend in * a safe manner. * * Main logic: when a post is saved, gather all the CSS generated by our blocks * and optimize them. Cache the optimized CSS in the post meta - this will be * used to load the CSS in the frontend head tag. * * This is done when a post is saved in order to pre-calculate all the CSS, * making it fast since we just need to load the saved CSS in the frontend * without any added processing. * * The styles (before being optimized) are saved as well, so we can make sure * first that the block's styles have been optimized before stripping them out * `render_block`. * * This is backward compatible, if no optimized CSS is found, then the original * blocks/content will just be used. * * Important notes: * - The optimized CSS is saved when the post is saved. * - The actual post content / blocks aren't modified. * - This works well with caching solutions like caching plugins * - No additional block rendering / processing is done (aside from stripping * the CSS in `render_block`) * - When previewing posts, no optimization is done. * * @since 3.3.0 */ // Exit if accessed directly. if ( ! defined( 'ABSPATH' ) ) { exit; } if ( ! class_exists( 'Stackable_CSS_Optimize' ) ) { /** * Stackable Optimize CSS. */ class Stackable_CSS_Optimize { /** * This holds the optimized CSS for the current post, this will be * loaded in the head. * * @var String */ public $optimized_css = ''; /** * This holds the raw CSS values of $this->optimized_css above, this is * used for cross checking whether a block's CSS was optimized and if * it's safe to strip out. * * @var array */ public $css_raw = array(); /** * This should be the order of the media queries to prevent wrong overrides. */ const MEDIA_QUERY_ORDER = array( '', // All screens, '@media screen and (min-width:1024px)', // Desktop only. '@media screen and (min-width:768px)', // Desktop & tablet. '@media screen and (min-width:768px) and (max-width:1023px)', // Tablet. '@media screen and (max-width:1023px)', // Tablet & mobile. '@media screen and (max-width:767px)', // Mobile. ); /** * Initialize */ function __construct() { // When a post is saved, generate an optimized version of the styles used. add_action( 'save_post', array( $this, 'generate_optimied_css_for_post' ), 10, 3 ); // Only do this when inline style optimization is enabled. // If stackable_optimize_inline_css === false (or option isn't // present), that's the default value (true) for the option. if ( ! is_admin() && get_option( 'stackable_optimize_inline_css' ) !== '' ) { // Load the optimized CSS in the head of posts. add_action( 'wp', array( $this, 'load_cached_css_for_post' ) ); // If the optimized CSS was loaded, then strip out the styles which were in the CSS. if ( is_frontend() ) { add_filter( 'render_block', array( $this, 'strip_optimized_block_styles' ), 10, 2 ); } } // Hide the CSS optimization custom fields because this will clutter the Block Editor. add_filter( 'is_protected_meta', array( $this, 'protect_optimized_css_meta' ), 10, 2 ); } /** * When editing a post, we need to generate the optimized CSS for all * the blocks used in the post. * * @param Int $post_id * @param WP_Post $post * @param Boolean $update * * @return void */ public function generate_optimied_css_for_post( $post_id, $post, $update ) { if ( $post->post_type === 'attachment' || $post->post_type === 'revision' || // Don't do this when previewing a post. $post->post_type === 'nav_menu_item' || $post->post_type === 'wp_template' || // DEV NOTE: This should work for FSE as well, but disallow this for now while we don't support it yet. $post->post_type === 'wp_template_part' || // DEV NOTE: This should work for FSE as well, but disallow this for now while we don't support it yet. $post->post_type === 'stackable_temp_post' ) { // Temporary post type used by Stackable for editing default blocks or UI Kits. return; } // If no contents, don't do anything. if ( empty( $post->post_content ) ) { return; } // Convert content to blocks. $blocks = parse_blocks( $post->post_content ); // Go through and gather all the styles. $styles = array(); // Holds unique ids and styles from blocks. self::parse_blocks( $blocks, $styles ); // Generate the optimized CSS. $styles_only = array(); foreach ( $styles as $block_styles ) { foreach ( $block_styles as $block_style ) { $styles_only[] = $block_style[1]; } } $optimized_css = count( $styles_only ) ? self::generate_css( $styles_only ) : ''; // Save the optimized CSS to the post if it changed. update_post_meta( $post_id, 'stackable_optimized_css', $optimized_css ); update_post_meta( $post_id, 'stackable_optimized_css_raw', $styles ); } /** * Goes through all the given blocks and gathers all the styles. * * @param Array $blocks * @param Array $style_arr Mutated array that will contain all the styles * * @return void */ public static function parse_blocks( $blocks, &$style_arr ) { foreach ( $blocks as $block ) { if ( isset( $block['blockName'] ) && strpos( $block['blockName'], 'stackable/' ) !== false ) { self::parse_block_style( $block, $style_arr ); } self::parse_blocks( $block['innerBlocks'], $style_arr ); } } /** * Parses a block and gathers all the styles. * * @param Array $block * @param Array $style_arr * * @return void */ public static function parse_block_style( $block, &$style_arr ) { $block_content = $block['innerHTML']; if ( stripos( $block_content, '<style' ) !== false ) { // We need the unique id for tracking. if ( is_array( $block['attrs'] ) && array_key_exists( 'uniqueId', $block['attrs'] ) ) { // Gather all the styles. preg_match_all( '#<style[^>]*>(.*?)</style>#', $block_content, $styles ); // $style contains: // 0 = whole style tag // 1 = css inside the style tag $all_block_styles = array(); foreach ( $styles[0] as $i => $style_tag ) { // If the styles contain dynamic content, let's not include // it in the optimized styles it because we need it dynamic. if ( stripos( $styles[1][ $i ], '!#stk_dynamic' ) ) { return; } // Add the styles to our list of styles to optimize. // Add only the css inside the style tag to minimize memory usage. $all_block_styles[] = array( $styles[0][ $i ], $styles[1][ $i ], ); } $unique_id = $block['attrs']['uniqueId']; $style_arr[ $unique_id ] = $all_block_styles; } } } /** * Gets the optimized CSS very early on for the current post, and * trigger the printing of the styles in the head. * * @return void */ public function load_cached_css_for_post() { // DEV NOTE: If we'll also do this for wp_template and // wp_template_part then we might need to use the actions: // render_block_core_template_part_post and // render_block_core_template_part_file $optimize_css = is_singular() && ! is_preview() && ! is_attachment(); $optimize_css = apply_filters( 'stackable/load_cached_css_for_post', $optimize_css ); if ( $optimize_css ) { $post_id = apply_filters( 'stackable/get_post_id_for_cached_css', get_the_ID() ); $this->optimized_css = get_post_meta( $post_id, 'stackable_optimized_css', true ); if ( ! empty( $this->optimized_css ) ) { $this->css_raw = get_post_meta( $post_id, 'stackable_optimized_css_raw', true ); add_action( 'wp_head', array( $this, 'print_optimized_styles' ) ); } } } /** * Prints the optimized CSS in the head. * * @return void */ public function print_optimized_styles() { if ( ! empty( $this->optimized_css ) ) { echo "\n"; echo '<style class="stk-block-styles">'; echo apply_filters( 'stackable_frontend_css', $this->optimized_css ); echo '</style>'; } } /** * HOVER STYLES HACK (1/4): The CSS generated by hover states in * src/util/styles/style-object.js get reordered because of * optimization. This stops the hover states from working. To fix it, * we add a placeholder to the styles for the hover states so we can * group them together. We move those with the placeholders to the end * to ensure that our hover styles work. * * @param string $a Selector * @param string $b Selector * * @return int -1, 0, 1 to reorder the array */ public static function selector_sort( $a, $b ) { if ( stripos( $a, '/* */' ) !== false && stripos( $b, '/* */' ) !== false ) { return 0; } else if ( stripos( $a, '/* */' ) !== false ) { return 1; } else if ( stripos( $b, '/* */' ) !== false ) { return -1; } return 0; } /** * Strips out the styles of a rendered block if the block's CSS has been * optimized in the head of the page. * * @param String $block_content * @param Array $block * * @return String The modified $block_content */ public function strip_optimized_block_styles( $block_content, $block ) { if ( $block_content === null ) { return $block_content; } if ( empty( $this->css_raw ) || empty( $this->optimized_css ) ) { return $block_content; } // Only do this to our blocks. if ( ! isset( $block['blockName'] ) || strpos( $block['blockName'], 'stackable/' ) === false ) { return $block_content; } if ( ! is_singular() || is_preview() ) { return $block_content; } if ( strpos( $block_content, '<style' ) === false ) { return $block_content; } // We need the unique id for tracking. if ( isset( $block['attrs']['uniqueId'] ) ) { $unique_id = $block['attrs']['uniqueId']; if ( isset( $this->css_raw[ $unique_id ] ) ) { foreach ( $this->css_raw[ $unique_id ] as $style ) { // $style[0] - contains the whole style tag. if ( is_array( $style ) ) { $block_content = str_replace( $style[0], '', $block_content ); } else if ( is_string( $style ) ) { $block_content = str_replace( '<style>' . $style . '</style>', '', $block_content ); } } } } return $block_content; } /** * Combines similar class selectors in a single :is() * * @param Array $selectors * @return Array Combined selectors */ public static function combine_selectors( $selectors ) { $new_selectors = array(); $classes_to_combine = array(); foreach( $selectors as $selector ) { $selector = trim( $selector ); // Find all the unique id classes of the form ".stk-123bcd" preg_match( '/(.stk-[a-f0-9-]{7})(?=\s|$)/', $selector, $matches ); // If it doesn't have a block selector that we can combine, just add it. if ( ! count( $matches ) ) { $new_selectors[] = $selector; continue; } $match = $matches[1]; // Don't do this if the selector is only the unique id. if ( $selector === $match ) { $new_selectors[] = $match; continue; } // Collect all the selectors we can combine. $selector = preg_replace( "#" . $match . "(?!-)#", '%s', $selector, 1 ); // Don't replace partial classname matches and only do it once. if ( ! array_key_exists( $selector, $classes_to_combine ) ) { $classes_to_combine[ $selector ] = array(); } $classes_to_combine[ $selector ][] = $match; } // Combine the selectors into a single :is() selector. foreach ( $classes_to_combine as $selector => $classes ) { if ( count( $classes ) === 1 ) { $new_selectors[] = sprintf( $selector, $classes[0] ); } else { $new_selectors[] = sprintf( $selector, ':is(' . implode( ', ', $classes ) . ')' ); } } return $new_selectors; } /** * Generates an optimized version of an array of CSS strings. * * @param Array An array of CSS strings. * * @return String The optimized CSS. */ public static function generate_css( $styles ) { // This contains styles as keys and selectors as values for easy // lookups. $all_style_rules = array(); // Organize the styles gathered so we can group together similar selectors. // The styles will be organized into: // - media query // - style rule // - selector // - selector 2 // - ... foreach ( $styles as $style ) { // Spread all media queries. // The CSS generated by blocks can have media queries with multiple selectors & rules inside. // To make optimization easier, we'll spread them out so we can sort the selectors per media queries. $style = preg_replace_callback( '#((@media[^\{]+\{)(.*?)\}})#', function( $style_matches ) { return preg_replace( '#\}([^\}])#', '}}' . $style_matches[2] . '$1', $style_matches[0] ); }, $style ); // Extract all media queries, selectors and rules for optimization. preg_match_all( '#(@\w+.*?\{)?(.*?)(\{[^\}]+\})\}?#', $style, $style_matches ); // $style_matches contains: // 1 = media query if there is one // 2 = selector // 3 = style rules foreach ( $style_matches[1] as $i => $media_query ) { $media_query = ! empty( $media_query ) ? substr( $media_query, 0, -1 ) : $media_query; $media_query = str_replace( 'width: ', 'width:', $media_query ); // Ensure that the media query is consistent. $selector = $style_matches[2][ $i ]; $style_rule = $style_matches[3][ $i ]; // HOVER STYLES HACK (2/4): Add a placeholder so we'll end // up grouping together the hover styles. if ( stripos( $selector, ':hover' ) !== false ) { $style_rule .= '/* */'; } if ( ! array_key_exists( $media_query, $all_style_rules ) ) { $all_style_rules[ $media_query ] = array(); } if ( ! array_key_exists( $style_rule, $all_style_rules[ $media_query ] ) ) { $all_style_rules[ $media_query ][ $style_rule ] = array(); } // We can have multiple selectors for the same rule. This // explodes by commas but doesn't do it for strings inside // parenthesis. This handles selectors like // ":is(g,rect,circle)" $selectors = preg_split( '#,(?![^(]+\))#', $selector ); foreach ( $selectors as $selector ) { $all_style_rules[ $media_query ][ $style_rule ][] = trim( $selector ); } } } // Organize the styles. $css = ''; $media_queries = self::MEDIA_QUERY_ORDER; // This will also include other media queries that we do not support, but just add those at the end of our CSS. foreach ( array_keys( $all_style_rules ) as $media_query ) { if ( ! in_array( $media_query, $media_queries ) ) { $media_queries[] = $media_query; } } // Go through each media query. foreach ( $media_queries as $media_query ) { if ( ! array_key_exists( $media_query, $all_style_rules ) ) { continue; } $styles = $all_style_rules[ $media_query ]; if ( ! empty( $media_query ) ) { $css .= $media_query . '{'; } // HOVER STYLES HACK (3/4): Reorder the selectors so hover // styles are put last. uksort( $styles, array( 'Stackable_CSS_Optimize', 'selector_sort' ) ); // Combine the selectors. foreach ( $styles as $style_rules => $selector_arr ) { // HOVER STYLES HACK (4/4): Remove the placeholder we used // to move the hover styles to the end. $style_rules = str_replace( '/* */', '', $style_rules ); // Optimize selectors by combining similar ones. $selector_arr = self::combine_selectors( $selector_arr ); $combined_css = implode( ',', $selector_arr ) . $style_rules; // Prevent invalid CSS in affecting the rest of the CSS // by closing any open curly braces. $open_curly_brace = substr_count( $combined_css , '{' ); $close_curly_brace = substr_count( $combined_css , '}' ); if ( $open_curly_brace > $close_curly_brace ) { $combined_css .= str_repeat( '}', $open_curly_brace - $close_curly_brace ); } $css .= $combined_css; } if ( ! empty( $media_query ) ) { $css .= '}'; } } return $css; } /** * Hide the CSS optimization custom fields because this will clutter the Block Editor. * * @param boolean $protected * @param string $meta_key * @return boolean */ public function protect_optimized_css_meta( $protected, $meta_key ) { if ( $meta_key === 'stackable_optimized_css' || $meta_key === 'stackable_optimized_css_raw' || $meta_key === 'stackable_css_hash' ) { return true; } return $protected; } } new Stackable_CSS_Optimize(); }
/home/batcwwjx/www/wp-content/plugins/stackable-ultimate-gutenberg-blocks/./src/css-optimize.php