PluginProbe
Block Animations, Motion & Scroll Effects – Ghost Kit / 3.7.2
Block Animations, Motion & Scroll Effects – Ghost Kit v3.7.2
3.7.2 3.7.1 3.7.0 3.6.1 trunk 1.6.3 2.25.0 3.3.0 3.3.1 3.3.2 3.3.3 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.6 3.5.0 3.5.1 3.6.0
← All changes | classes/class-breakpoints.php +246 -172 3.3.03.7.2 View file →
@@ -4,26 +4,8 @@
4 4 *
5 5 * @package ghostkit
6 6 */
7 7
8 -if ( ! function_exists( 'is_plugin_active' ) ) {
9 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
10 -}
11 -
12 -// phpcs:ignore
13 -$pro_version = false;
14 -
15 -if ( is_plugin_active( 'ghostkit-pro/class-ghost-kit-pro.php' ) ) {
16 - // phpcs:ignore
17 - $pro_data = get_plugin_data( ABSPATH . 'wp-content/plugins/ghostkit-pro/class-ghost-kit-pro.php' );
18 - // phpcs:ignore
19 - $pro_version = isset( $pro_data['Version'] ) ? $pro_data['Version'] : false;
20 -}
21 -
22 -if ( $pro_version && version_compare( $pro_version, '1.6.2', '<=' ) ) {
23 - require_once ghostkit()->plugin_path . 'classes/class-breakpoints-fallback.php';
24 -}
25 -
26 8 if ( ! class_exists( 'GhostKit_Breakpoints' ) ) {
27 9 /**
28 10 * GhostKit_Breakpoints class
29 11 */
@@ -57,171 +39,325 @@
57 39 */
58 40 protected static $default_lg = 1200;
59 41
60 42 /**
61 - * Database saved hash option Name.
43 + * Plugin name. Also the directory under uploads that holds the generated CSS.
62 44 *
63 45 * @var string
64 46 */
65 - protected $database_saved_hash_option_name = 'ghostkit_saved_breakpoints_hash';
47 + protected $plugin_name = 'ghostkit';
66 48
67 49 /**
68 - * Plugin name.
50 + * Plugin version.
69 51 *
70 52 * @var string
71 53 */
72 - protected $plugin_name = 'ghostkit';
54 + protected $plugin_version = GHOSTKIT_VERSION;
73 55
74 56 /**
75 - * Plugin version.
57 + * GhostKit_Breakpoints constructor.
58 + */
59 + public function __construct() {
60 + add_filter( 'style_loader_src', array( $this, 'change_style_src_to_compile' ), 10, 1 );
61 + add_action( 'gkt_before_assets_register', array( $this, 'maybe_generate_css' ) );
62 + }
63 +
64 + /**
65 + * Get plugin path.
76 66 *
77 - * @var string
67 + * @return string
78 68 */
79 - protected $plugin_version = GHOSTKIT_VERSION;
69 + public function get_plugin_path() {
70 + return ghostkit()->plugin_path;
71 + }
80 72
81 73 /**
82 - * Scss Configurations.
74 + * Get plugin url.
83 75 *
84 - * @var array
76 + * @return string
85 77 */
86 - protected $scss_configs = array(
87 - 'gutenberg/style.scss',
88 - 'gutenberg/editor.scss',
89 - 'gutenberg/blocks/*/styles/style.scss',
90 - );
78 + public function get_plugin_url() {
79 + return ghostkit()->plugin_url;
80 + }
91 81
92 82 /**
93 - * Compile Scss Configurations.
83 + * Name of the option that holds the hash and the list of generated files.
94 84 *
95 - * @var array
85 + * @return string
96 86 */
97 - protected $compile_scss_configs;
87 + protected function get_option_name() {
88 + return str_replace( '-', '_', $this->plugin_name ) . '_breakpoints_css';
89 + }
98 90
99 91 /**
100 - * Background breakpoints processing.
92 + * Directory under uploads that holds the generated CSS.
101 93 *
102 - * @var object
94 + * @return string
103 95 */
104 - protected $breakpoints_background_process;
96 + protected function get_output_dir() {
97 + $upload_dir = wp_get_upload_dir();
105 98
99 + return $upload_dir['basedir'] . '/' . $this->plugin_name;
100 + }
101 +
106 102 /**
107 - * GhostKit_Breakpoints constructor.
103 + * Stored hash and list of generated files.
104 + *
105 + * @return array|false
108 106 */
109 - public function __construct() {
110 - if ( ! class_exists( 'GhostKit_Breakpoints_Background' ) ) {
111 - // breakpoints background.
112 - require_once __DIR__ . '/class-breakpoints-background.php';
107 + protected function get_generated_css() {
108 + $stored = get_option( $this->get_option_name() );
109 +
110 + if (
111 + ! is_array( $stored ) ||
112 + ! isset( $stored['hash'], $stored['files'] ) ||
113 + ! is_array( $stored['files'] )
114 + ) {
115 + return false;
113 116 }
114 117
115 - $this->compile_scss_configs = $this->get_compile_scss_configs();
116 - $this->breakpoints_background_process = new GhostKit_Breakpoints_Background();
118 + return $stored;
119 + }
117 120
118 - add_filter( 'style_loader_src', array( $this, 'change_style_src_to_compile' ), 10, 1 );
119 - add_action( 'gkt_before_assets_register', array( $this, 'maybe_compile_scss_files' ) );
121 + /**
122 + * Regenerate the CSS when the breakpoints or the plugin version changed.
123 + *
124 + * @return void
125 + */
126 + public function maybe_generate_css() {
127 + $breakpoints = self::get_breakpoints();
128 + $hash = $this->get_breakpoints_hash( $breakpoints );
129 + $stored = $this->get_generated_css();
130 +
131 + if ( $hash === $this->get_default_breakpoints_hash() ) {
132 + if ( $stored ) {
133 + $this->remove_generated_css();
134 + }
135 + return;
136 + }
137 +
138 + if ( $stored && $stored['hash'] === $hash ) {
139 + return;
140 + }
141 +
142 + $this->remove_generated_css();
143 +
144 + update_option(
145 + $this->get_option_name(),
146 + array(
147 + 'hash' => $hash,
148 + 'files' => $this->generate_css( $breakpoints ),
149 + )
150 + );
120 151 }
121 152
122 153 /**
123 - * Get plugin path.
154 + * Write the built CSS files with the breakpoint values replaced into uploads.
124 155 *
125 - * @return string
156 + * @param array $breakpoints - Breakpoints.
157 + * @return array Paths of the written files, relative to the plugin directory.
126 158 */
127 - public function get_plugin_path() {
128 - return ghostkit()->plugin_path;
159 + protected function generate_css( $breakpoints ) {
160 + $plugin_path = wp_normalize_path( $this->get_plugin_path() );
161 + $build_dir = $plugin_path . 'build';
162 + $output_dir = $this->get_output_dir();
163 + $files = array();
164 +
165 + if ( ! is_dir( $build_dir ) ) {
166 + return $files;
167 + }
168 +
169 + try {
170 + // CATCH_GET_CHILD skips a subdirectory PHP cannot open instead of ending the walk.
171 + $iterator = new RecursiveIteratorIterator(
172 + new RecursiveDirectoryIterator( $build_dir, FilesystemIterator::SKIP_DOTS ),
173 + RecursiveIteratorIterator::LEAVES_ONLY,
174 + RecursiveIteratorIterator::CATCH_GET_CHILD
175 + );
176 +
177 + foreach ( $iterator as $file ) {
178 + if ( 'css' !== $file->getExtension() ) {
179 + continue;
180 + }
181 +
182 + $path = wp_normalize_path( $file->getPathname() );
183 + // phpcs:ignore
184 + $css = file_get_contents( $path );
185 +
186 + if ( false === $css ) {
187 + continue;
188 + }
189 +
190 + $replaced = self::replace_breakpoints( $css, $breakpoints );
191 +
192 + if ( $replaced === $css ) {
193 + continue;
194 + }
195 +
196 + $relative = substr( $path, strlen( $plugin_path ) );
197 + $target = $output_dir . '/' . $relative;
198 +
199 + if ( ! wp_mkdir_p( dirname( $target ) ) ) {
200 + continue;
201 + }
202 +
203 + // phpcs:ignore
204 + if ( false === file_put_contents( $target, $replaced ) ) {
205 + continue;
206 + }
207 +
208 + $files[] = $relative;
209 + }
210 + } catch ( UnexpectedValueException $e ) {
211 + // `build` itself could not be opened; nothing was written.
212 + unset( $e );
213 + }
214 +
215 + // WordPress derives the `-rtl.css` URL from the served one, so a file whose RTL twin
216 + // exists in the build but was not written would give RTL sites a 404. Serve neither.
217 + foreach ( $files as $index => $relative ) {
218 + $rtl_twin = preg_replace( '/\.css$/', '-rtl.css', $relative );
219 +
220 + if (
221 + $rtl_twin !== $relative &&
222 + ! in_array( $rtl_twin, $files, true ) &&
223 + file_exists( $plugin_path . $rtl_twin )
224 + ) {
225 + wp_delete_file( $output_dir . '/' . $relative );
226 + unset( $files[ $index ] );
227 + }
228 + }
229 +
230 + $files = array_values( $files );
231 + sort( $files );
232 +
233 + return $files;
129 234 }
130 235
131 236 /**
132 - * Get plugin url.
237 + * Remove the generated CSS and the option that lists it.
133 238 *
134 - * @return string
239 + * @return void
135 240 */
136 - public function get_plugin_url() {
137 - return ghostkit()->plugin_url;
241 + protected function remove_generated_css() {
242 + self::remove_dir( $this->get_output_dir() . '/build' );
243 + delete_option( $this->get_option_name() );
138 244 }
139 245
140 246 /**
141 - * Get compile scss configurations.
247 + * Delete a directory with everything inside it.
142 248 *
143 - * @return array
249 + * @param string $dir - Absolute path.
250 + * @return void
144 251 */
145 - protected function get_compile_scss_configs() {
146 - $plugin_path = $this->get_plugin_path();
147 - $upload_dir = wp_upload_dir();
148 - $compile_scss_configs = array();
149 - $scss_paths = apply_filters( 'gkt_scss_paths', $plugin_path );
150 - $scss_configs = apply_filters( 'gkt_scss_configs', $this->scss_configs );
252 + public static function remove_dir( $dir ) {
253 + if ( ! is_dir( $dir ) ) {
254 + return;
255 + }
151 256
152 - if ( ! is_array( $scss_paths ) ) {
153 - $scss_paths = array( $scss_paths );
154 - }
257 + try {
258 + $iterator = new RecursiveIteratorIterator(
259 + new RecursiveDirectoryIterator( $dir, FilesystemIterator::SKIP_DOTS ),
260 + RecursiveIteratorIterator::CHILD_FIRST
261 + );
155 262
156 - foreach ( $scss_configs as $scss_config ) {
157 - foreach ( $scss_paths as $path ) {
158 - $compile_scss_configs = $this->get_parsed_scss_files( $compile_scss_configs, $path, $upload_dir, $scss_config );
263 + foreach ( $iterator as $item ) {
264 + if ( $item->isDir() ) {
265 + // phpcs:ignore
266 + rmdir( $item->getPathname() );
267 + } else {
268 + wp_delete_file( $item->getPathname() );
269 + }
159 270 }
271 + } catch ( UnexpectedValueException $e ) {
272 + // A directory PHP cannot open stays in place, with the directories above it.
273 + return;
160 274 }
161 275
162 - return $compile_scss_configs;
276 + // phpcs:ignore
277 + rmdir( $dir );
163 278 }
164 279
165 280 /**
166 - * Get parsed array of scss files
281 + * Replace the default breakpoint values inside `@media` preludes with the given ones.
167 282 *
168 - * @param array $compile_scss_configs - Array of config width scss files.
169 - * @param string $scss_path - Path to scss files.
170 - * @param string $upload_dir - Uploas WP dir.
171 - * @param string $scss_config - File search mask.
172 - * @return array
283 + * Only `(min-width: Npx)` and `(max-width: Npx)` whose N is a default breakpoint change,
284 + * in one pass, so a custom value equal to another default is never replaced twice.
285 + *
286 + * @param string $css - CSS to process.
287 + * @param array $breakpoints - Breakpoints keyed xs, sm, md, lg.
288 + * @return string
173 289 */
174 - protected function get_parsed_scss_files( &$compile_scss_configs, $scss_path, $upload_dir, $scss_config ) {
175 - foreach ( glob( $scss_path . $scss_config ) as $template ) {
176 - $output_file = str_replace( $scss_path, $upload_dir['basedir'] . '/' . $this->plugin_name . '/', $template );
177 - $output_file = str_replace( '.scss', '.min.css', $output_file );
290 + public static function replace_breakpoints( $css, $breakpoints ) {
291 + $defaults = array(
292 + 'xs' => self::$default_xs,
293 + 'sm' => self::$default_sm,
294 + 'md' => self::$default_md,
295 + 'lg' => self::$default_lg,
296 + );
297 + $map = array();
178 298
179 - $compile_scss_configs[] = array(
180 - 'input_file' => $template,
181 - 'output_file' => $output_file,
182 - );
299 + foreach ( $defaults as $name => $default ) {
300 + if ( isset( $breakpoints[ $name ] ) && is_numeric( $breakpoints[ $name ] ) ) {
301 + // Themes pass fractions such as 777.98 to keep a max-width query from
302 + // overlapping the min-width one, so the value is kept as given.
303 + $map[ $default ] = (string) ( 0 + $breakpoints[ $name ] );
304 + }
183 305 }
184 - return $compile_scss_configs;
306 +
307 + return preg_replace_callback(
308 + '/@media[^{;]*\{/',
309 + function ( $prelude ) use ( $map ) {
310 + return preg_replace_callback(
311 + '/\(\s*(min|max)-width\s*:\s*(\d+)px\s*\)/',
312 + function ( $query ) use ( $map ) {
313 + $value = (int) $query[2];
314 +
315 + if ( ! isset( $map[ $value ] ) ) {
316 + return $query[0];
317 + }
318 +
319 + return '(' . $query[1] . '-width:' . $map[ $value ] . 'px)';
320 + },
321 + $prelude[0]
322 + );
323 + },
324 + $css
325 + );
185 326 }
186 327
187 328 /**
188 - * Change style src to compile css files.
329 + * Serve the generated CSS instead of the plugin file when custom breakpoints are set.
189 330 *
190 331 * @param string $src - Url to style.
191 332 * @return string
192 333 */
193 334 public function change_style_src_to_compile( $src ) {
194 - $plugin_url = $this->get_plugin_url();
195 - $breakpoints = self::get_breakpoints();
196 - $breakpoints_hash = $this->get_breakpoints_hash( $breakpoints );
197 - $default_breakpoints_hash = $this->get_default_breakpoints_hash();
335 + $stored = $this->get_generated_css();
198 336
199 - if ( $breakpoints_hash !== $default_breakpoints_hash ) {
200 - $is_plugin_file = strstr( $src, $plugin_url );
337 + if ( ! $stored || empty( $stored['files'] ) ) {
338 + return $src;
339 + }
201 340
202 - if ( $is_plugin_file ) {
203 - $configs = $this->compile_scss_configs;
204 - $relative_uri = str_replace( $plugin_url, '', $is_plugin_file );
205 - $relative_path = explode( '?ver=', $relative_uri )[0];
206 - $upload_dir = wp_upload_dir();
207 - $output_file = $upload_dir['basedir'] . '/' . $this->plugin_name . '/' . $relative_path;
341 + $plugin_url = $this->get_plugin_url();
208 342
209 - foreach ( $configs as $config ) {
210 - if (
211 - $config['output_file'] === $output_file &&
212 - file_exists( $output_file )
213 - ) {
214 - // add hash to compiled CSS version to prevent problems with cache.
215 - $uri_with_hash_version = str_replace( '?ver=', '?ver=' . $breakpoints_hash . '.', $relative_uri );
343 + if ( 0 !== strpos( $src, $plugin_url ) ) {
344 + return $src;
345 + }
216 346
217 - $src = $upload_dir['baseurl'] . '/' . $this->plugin_name . '/' . $uri_with_hash_version;
218 - }
219 - }
220 - }
347 + $path = strtok( substr( $src, strlen( $plugin_url ) ), '?' );
348 +
349 + if ( ! in_array( $path, $stored['files'], true ) ) {
350 + return $src;
221 351 }
222 352
223 - return $src;
353 + if ( ! file_exists( $this->get_output_dir() . '/' . $path ) ) {
354 + return $src;
355 + }
356 +
357 + $upload_dir = wp_get_upload_dir();
358 +
359 + return add_query_arg( 'ver', $stored['hash'], $upload_dir['baseurl'] . '/' . $this->plugin_name . '/' . $path );
224 360 }
225 361
226 362 /**
227 363 * Get Breakpoints.
@@ -293,70 +429,8 @@
293 429 'md' => self::$default_md,
294 430 'lg' => self::$default_lg,
295 431 )
296 432 );
297 - }
298 -
299 - /**
300 - * Styles may need to be compiled if breakpoints have been changed.
301 - *
302 - * @return void
303 - */
304 - public function maybe_compile_scss_files() {
305 - $breakpoints = self::get_breakpoints();
306 - $breakpoints_hash = $this->get_breakpoints_hash( $breakpoints );
307 - $default_breakpoints_hash = $this->get_default_breakpoints_hash();
308 - $saved_breakpoints_hash = get_option( $this->database_saved_hash_option_name );
309 -
310 - if (
311 - $breakpoints_hash !== $saved_breakpoints_hash &&
312 - $breakpoints_hash !== $default_breakpoints_hash
313 - ) {
314 - $compile_scss_configs = $this->compile_scss_configs;
315 -
316 - // Replace modules in all SCSS files.
317 - if ( ! empty( $compile_scss_configs ) ) {
318 - $plugin_path = $this->get_plugin_path();
319 -
320 - $scss_paths = apply_filters( 'gkt_scss_paths', $plugin_path );
321 -
322 - if ( ! is_array( $scss_paths ) ) {
323 - $scss_paths = array( $scss_paths );
324 - }
325 -
326 - foreach ( $scss_paths as $path ) {
327 - new GhostKit_Scss_Replace_Modules( $path );
328 - }
329 - }
330 -
331 - $breakpoints = self::get_breakpoints();
332 -
333 - $variables = array(
334 - 'media-xs' => $breakpoints['xs'] . 'px',
335 - 'media-sm' => $breakpoints['sm'] . 'px',
336 - 'media-md' => $breakpoints['md'] . 'px',
337 - 'media-lg' => $breakpoints['lg'] . 'px',
338 - );
339 -
340 - foreach ( $compile_scss_configs as $compile_scss_config ) {
341 - $scss_config_arguments = array_merge(
342 - $compile_scss_config,
343 - array(
344 - 'variables' => $variables,
345 - )
346 - );
347 -
348 - if ( ! file_exists( $compile_scss_config['output_file'] ) ) {
349 - new GhostKit_Scss_Compiler( $scss_config_arguments );
350 - } else {
351 - $this->breakpoints_background_process->push_to_queue( $scss_config_arguments );
352 - }
353 - }
354 -
355 - $this->breakpoints_background_process->save()->dispatch();
356 -
357 - update_option( $this->database_saved_hash_option_name, $breakpoints_hash );
358 - }
359 433 }
360 434
361 435 /**
362 436 * Get Default Extra Small Breakpoint.