PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0-beta3
Elementor Website Builder – more than just a page builder v4.3.0-beta3
4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 All 452 releases
elementor / modules / atomic-widgets / styles / atomic-styles-manager.php

atomic-styles-manager.php in Elementor Website Builder – more than just a page builder 4.3.0-beta3, at modules/atomic-widgets/styles/atomic-styles-manager.php

270 lines 7.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\AtomicWidgets\Styles;
4
5 use Elementor\Core\Base\Document;
6 use Elementor\Core\Breakpoints\Breakpoint;
7 use Elementor\Core\Utils\Collection;
8 use Elementor\Modules\AtomicWidgets\Utils\Memo;
9 use Elementor\Modules\AtomicWidgets\Styles\CacheValidity\Cache_Validity;
10 use Elementor\Plugin;
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit; // Exit if accessed directly.
14 }
15
16 class Atomic_Styles_Manager {
17 private static ?self $instance = null;
18
19 /**
20 * @var array<string, array{styles: callable, path: array<string>}>
21 */
22 private array $registered_styles_by_key = [];
23
24 private Cache_Validity $cache_validity;
25
26 private array $post_ids = [];
27
28 private CSS_Files_Manager $css_files_manager;
29
30 const DEFAULT_BREAKPOINT = 'desktop';
31
32 private array $fonts = [];
33
34 public function __construct() {
35 $this->css_files_manager = new CSS_Files_Manager();
36 $this->cache_validity = new Cache_Validity();
37 }
38
39 public static function instance() {
40 if ( ! self::$instance ) {
41 self::$instance = new self();
42 }
43
44 return self::$instance;
45 }
46
47 public function register_hooks() {
48 add_action( 'elementor/frontend/after_enqueue_post_styles', fn() => $this->enqueue_styles() );
49
50 add_action( 'elementor/post/render', function( $post_id ) {
51 $this->post_ids[] = $post_id;
52 } );
53
54 add_action( 'elementor/atomic-widgets/styles/clear', fn( array $path ) => $this->clear_styles( $path ) );
55 }
56
57 public function register( array $path, callable $get_style_defs ) {
58 $key = $this->convert_path_to_handle( $path );
59
60 $this->registered_styles_by_key[ $key ] = [
61 'get_styles' => $get_style_defs,
62 'path' => $path,
63 ];
64 }
65
66 private function enqueue_styles() {
67 if ( empty( $this->post_ids ) ) {
68 return;
69 }
70
71 do_action( 'elementor/atomic-widgets/styles/register', $this, $this->post_ids );
72
73 $get_styles_memo = new Memo();
74
75 $styles_by_key = Collection::make( $this->registered_styles_by_key )
76 ->map_with_keys( fn ( $style_params, $style_key ) => [
77 $style_key => [
78 'get_styles' => $get_styles_memo->memoize( $style_key, $style_params['get_styles'] ),
79 'path' => $style_params['path'],
80 ],
81 ])
82 ->all();
83
84 $this->before_render( $styles_by_key );
85
86 $this->render( $styles_by_key );
87
88 $this->after_render( $styles_by_key );
89 }
90
91 private function before_render( array $styles_by_key ) {
92 $this->fonts = [];
93
94 foreach ( $styles_by_key as $style_key => $style_params ) {
95 $path = $style_params['path'];
96
97 // This cache validity check is of the general style, and used to reset dependencies that can only be evaluated
98 // upon the style rendering flow (i.e. when cache is invalid).
99 // (the corresponding css files cache validity includes also the file's breakpoint in the cache keys array)
100 if ( ! $this->cache_validity->is_valid( $path ) ) {
101 Style_Fonts::make( $style_key )->clear();
102
103 // We should validate it after this iteration
104 $this->cache_validity->validate( $path, uniqid() );
105 }
106 }
107 }
108
109 private function render( array $styles_by_key ) {
110 $group_by_breakpoint_memo = new Memo();
111 $breakpoints = $this->get_breakpoints();
112
113 foreach ( $breakpoints as $breakpoint_key ) {
114 foreach ( $styles_by_key as $style_key => $style_params ) {
115 $path = $style_params['path'];
116 $render_css = fn() => $this->render_css_by_breakpoints( $style_params['get_styles'], $style_key, $breakpoint_key, $group_by_breakpoint_memo );
117
118 $version = $this->cache_validity->get_meta( $path );
119
120 $breakpoint_media = $this->get_breakpoint_media( $breakpoint_key );
121
122 if ( ! $breakpoint_media ) {
123 continue;
124 }
125
126 $breakpoint_path = array_merge( $path, [ $breakpoint_key ] );
127
128 // `CSS_Files_Manager::get()` owns the per-breakpoint decision: it consults the
129 // cache-validity leaf under `$breakpoint_path` (including the `should_exist`
130 // meta), regenerates when the cache is invalid or when a should-be-present file
131 // is missing on disk, and validates the leaf on success. See ED-24903.
132 $style_file = $this->css_files_manager->get(
133 $this->convert_path_to_handle( $breakpoint_path ),
134 $breakpoint_media,
135 $render_css,
136 $breakpoint_path
137 );
138
139 if ( ! $style_file ) {
140 continue;
141 }
142
143 wp_enqueue_style(
144 $style_file->get_handle(),
145 $style_file->get_url(),
146 [],
147 $version,
148 $style_file->get_media()
149 );
150 }
151 }
152 }
153
154 private function render_css( array $styles, string $style_key ) {
155 $style_fonts = Style_Fonts::make( $style_key );
156
157 return Styles_Renderer::make(
158 Plugin::$instance->breakpoints->get_breakpoints_config()
159 )->on_font_enqueue( fn( $font ) => $style_fonts->add( $font ) )
160 ->render( $styles );
161 }
162
163 private function get_breakpoint_media( string $breakpoint_key ): ?string {
164 $breakpoint_config = Plugin::$instance->breakpoints->get_breakpoints_config()[ $breakpoint_key ] ?? null;
165
166 return $breakpoint_config ? Styles_Renderer::get_media_query( $breakpoint_config ) : 'all';
167 }
168
169 private function render_css_by_breakpoints( callable $get_styles, string $style_key, string $breakpoint_key, Memo $group_by_breakpoint_memo ) {
170 $memo_key = $style_key . '-' . $breakpoint_key;
171 $get_grouped_styles = $group_by_breakpoint_memo->memoize( $memo_key, fn() => $this->group_by_breakpoint( $get_styles() ) );
172 $grouped_styles = $get_grouped_styles();
173
174 return $this->render_css( $grouped_styles[ $breakpoint_key ] ?? [], $style_key );
175 }
176
177 private function group_by_breakpoint( $styles ) {
178 return Collection::make( $styles )->reduce( function( $group, $style ) {
179 Collection::make( $style['variants'] )->each( function( $variant ) use ( &$group, $style ) {
180 $breakpoint = $variant['meta']['breakpoint'] ?? self::DEFAULT_BREAKPOINT;
181
182 if ( empty( $breakpoint ) ) {
183 $breakpoint = self::DEFAULT_BREAKPOINT;
184 }
185
186 if ( ! isset( $group[ $breakpoint ][ $style['id'] ] ) ) {
187 $style_for_breakpoint = [
188 'id' => $style['id'],
189 'type' => $style['type'],
190 'variants' => [],
191 ];
192
193 if ( isset( $style['cssName'] ) ) {
194 $style_for_breakpoint['cssName'] = $style['cssName'];
195 }
196
197 $group[ $breakpoint ][ $style['id'] ] = $style_for_breakpoint;
198 }
199
200 $group[ $breakpoint ][ $style['id'] ]['variants'][] = $variant;
201 } );
202
203 return $group;
204 }, [] );
205 }
206
207 private function get_breakpoints() {
208 return Collection::make( Plugin::$instance->breakpoints->get_breakpoints() )
209 ->map( fn( Breakpoint $breakpoint ) => $breakpoint->get_name() )
210 ->reverse()
211 ->prepend( self::DEFAULT_BREAKPOINT )
212 ->all();
213 }
214
215 private function after_render( array $styles_by_key ) {
216 foreach ( $styles_by_key as $style_key => $style_params ) {
217 $this->add_fonts_to_enqueue( $style_key );
218 }
219
220 $this->enqueue_fonts();
221 }
222
223 private function add_fonts_to_enqueue( string $style_key ) {
224 $style_fonts = Style_Fonts::make( $style_key );
225
226 $this->fonts = array_unique(
227 array_merge(
228 $this->fonts,
229 array_values( $style_fonts->get() )
230 )
231 );
232 }
233
234 private function enqueue_fonts() {
235 foreach ( $this->fonts as $font ) {
236 Plugin::instance()->frontend->enqueue_font( $font );
237 }
238 }
239
240 private function clear_styles( array $path ) {
241 $node = $this->cache_validity->get_node( $path );
242
243 $this->clear_styles_by_node( $path, $node );
244
245 $this->cache_validity->invalidate( $path );
246 }
247
248 private function clear_styles_by_node( array $path, $node ) {
249 if ( ! $node ) {
250 return;
251 }
252
253 if ( true === $node || $node['state'] ) {
254 $this->css_files_manager->delete( $this->convert_path_to_handle( $path ) );
255 }
256
257 if ( is_bool( $node ) || empty( $node['children'] ) ) {
258 return;
259 }
260
261 foreach ( $node['children'] as $child_key => $child_node ) {
262 $this->clear_styles_by_node( array_merge( $path, [ $child_key ] ), $child_node );
263 }
264 }
265
266 private function convert_path_to_handle( array $path ) {
267 return implode( '-', $path );
268 }
269 }
270