PluginProbe
Elementor Website Builder – more than just a page builder / 3.32.0-dev3
Elementor Website Builder – more than just a page builder v3.32.0-dev3
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
← All changes | modules/global-classes/atomic-global-styles.php +33 -355 4.2.23.32.0-dev3 View file →
@@ -1,413 +1,91 @@
1 1 <?php
2 2
3 3 namespace Elementor\Modules\GlobalClasses;
4 4
5 -use Elementor\Modules\AtomicWidgets\Styles\CacheValidity\Cache_Validity;
6 5 use Elementor\Plugin;
7 6 use Elementor\Modules\AtomicWidgets\Styles\Atomic_Styles_Manager;
7 +use Elementor\Modules\AtomicWidgets\Cache_Validity;
8 8
9 9 class Atomic_Global_Styles {
10 10 const STYLES_KEY = 'global';
11 - const RELATED_KEY = 'related';
12 - const RELATED_REVERSE_KEY = 'related-reverse';
13 11
14 - private Global_Classes_Relations $relations;
15 -
16 - public function __construct( Global_Classes_Relations $relations ) {
17 - $this->relations = $relations;
18 - }
19 -
20 12 public function register_hooks() {
21 13 add_action(
22 14 'elementor/atomic-widgets/styles/register',
23 - fn( Atomic_Styles_Manager $styles_manager, array $post_ids ) => $this->register_styles( $styles_manager, $post_ids ),
15 + fn( Atomic_Styles_Manager $styles_manager, array $post_ids ) => $this->register_styles( $styles_manager ),
24 16 20,
25 17 2
26 18 );
27 19
28 - add_action(
29 - 'elementor/global_classes/update',
30 - fn( string $context, array $changes ) => $this->invalidate_cache_for_updated_classes( $context, $changes ),
31 - 10,
32 - 2
33 - );
20 + add_action( 'elementor/global_classes/update', fn( string $context ) => $this->invalidate_cache( $context ), 10, 1 );
34 21
35 22 add_action(
36 23 'deleted_post',
37 - fn( $post_id ) => $this->on_kit_delete( $post_id )
24 + fn( $post_id ) => $this->on_post_delete( $post_id )
38 25 );
39 26
40 27 add_action(
41 28 'elementor/core/files/clear_cache',
42 - fn() => $this->invalidate_all_cache(),
29 + fn() => $this->invalidate_cache(),
43 30 );
44 31
45 - add_filter(
46 - 'elementor/atomic-widgets/settings/transformers/classes',
32 + add_filter('elementor/atomic-widgets/settings/transformers/classes',
47 33 fn( $value ) => $this->transform_classes_names( $value )
48 34 );
49 -
50 - add_action(
51 - 'elementor/document/after_save',
52 - fn( $document ) => $this->on_document_save( $document )
53 - );
54 35 }
55 36
56 - private function register_styles( Atomic_Styles_Manager $styles_manager, array $post_ids ) {
57 - $context = $this->get_context();
37 + private function register_styles( Atomic_Styles_Manager $styles_manager ) {
38 + $context = is_preview() ? Global_Classes_Repository::CONTEXT_PREVIEW : Global_Classes_Repository::CONTEXT_FRONTEND;
58 39
59 - $parent_to_embedded = [];
60 - $visited = [];
40 + $get_styles = function () use ( $context ) {
41 + return Global_Classes_Repository::make()->context( $context )->all()->get_items()->map( function( $item ) {
42 + $item['id'] = $item['label'];
43 + return $item;
44 + })->all();
45 + };
61 46
62 - foreach ( $post_ids as $post_id ) {
63 - $this->resolve_embedded_post_descendants( (int) $post_id, $parent_to_embedded, $visited );
64 - }
65 -
66 - $this->persist_relation_maps( $parent_to_embedded, $context );
67 -
68 - $all_embedded = [];
69 - foreach ( $parent_to_embedded as $children ) {
70 - $all_embedded = array_merge( $all_embedded, $children );
71 - }
72 - $all_embedded = array_flip( array_unique( $all_embedded ) ); // use as set
73 -
74 - foreach ( $post_ids as $post_id ) {
75 - $post_id_int = (int) $post_id;
76 -
77 - if ( isset( $all_embedded[ $post_id_int ] ) ) {
78 - // This post is embedded inside another rendered post – skip it so
79 - // it doesn't produce its own global-{id}-*.css file.
80 - continue;
81 - }
82 -
83 - $embedded_ids = $parent_to_embedded[ $post_id_int ] ?? [];
84 - $aggregate_ids = array_merge( [ $post_id_int ], $embedded_ids );
85 -
86 - $get_styles = fn() => $this->get_document_global_styles( $aggregate_ids, $context );
87 -
88 - $styles_manager->register(
89 - [ $this->get_cache_root_key(), $post_id, $context ],
90 - $get_styles
91 - );
92 - }
47 + $styles_manager->register(
48 + self::STYLES_KEY . '-' . $context,
49 + $get_styles,
50 + [ self::STYLES_KEY, $context ]
51 + );
93 52 }
94 53
95 - /**
96 - * Recursively resolve embedded post descendants for a parent post id.
97 - *
98 - * Applies the `elementor/document/related_posts` filter transitively and
99 - * guards against cycles with the shared $visited set.
100 - *
101 - * @param int $pid Parent post id being inspected.
102 - * @param array<int,int[]> $parent_to_embedded Accumulated forward map.
103 - * @param array<int,true> $visited Cycle guard.
104 - * @return int[] Embedded descendant post ids to merge into $pid's global styles.
105 - */
106 - private function resolve_embedded_post_descendants( int $pid, array &$parent_to_embedded, array &$visited ): array {
107 - if ( isset( $visited[ $pid ] ) ) {
108 - return $parent_to_embedded[ $pid ] ?? [];
109 - }
110 -
111 - $visited[ $pid ] = true;
112 -
113 - $related = (array) apply_filters( 'elementor/document/related_posts', [], $pid );
114 - $related = array_values( array_unique( array_map( 'intval', array_filter( $related, 'is_numeric' ) ) ) );
115 -
116 - $all_related = $related;
117 -
118 - foreach ( $related as $related_post ) {
119 - $further_related_posts = $this->resolve_embedded_post_descendants( $related_post, $parent_to_embedded, $visited );
120 - $all_related = array_values( array_unique( array_merge( $all_related, $further_related_posts ) ) );
121 - }
122 -
123 - $parent_to_embedded[ $pid ] = $all_related;
124 -
125 - return $all_related;
126 - }
127 -
128 - /**
129 - * Returns the merged, ordered global class styles for one or more post ids.
130 - *
131 - * @param int[] $post_ids One or more post ids whose classes should be merged.
132 - * @param string $context Frontend or preview context.
133 - * @return array Ordered style items ready for serialization.
134 - */
135 - private function get_document_global_styles( array $post_ids, string $context ): array {
136 - $is_preview = Global_Classes_Repository::CONTEXT_PREVIEW === $context;
137 -
138 - $class_ids = [];
139 - foreach ( $post_ids as $pid ) {
140 - $ids = $this->relations->set_preview( $is_preview )->get_styles_by_post( (int) $pid );
141 - $class_ids = array_merge( $class_ids, $ids );
142 - }
143 - $class_ids = array_values( array_unique( $class_ids ) );
144 -
145 - if ( empty( $class_ids ) ) {
146 - return [];
147 - }
148 -
149 - $repository = Global_Classes_Repository::make();
150 -
151 - if ( $is_preview ) {
152 - $repository->set_preview( true );
153 - }
154 -
155 - $global_order = $repository->all_labels();
156 - $ordered_class_ids = array_values( array_intersect( array_keys( $global_order ), $class_ids ) );
157 -
158 - if ( empty( $ordered_class_ids ) ) {
159 - return [];
160 - }
161 -
162 - $items = $repository->get_by_ids( $ordered_class_ids );
163 - $reversed_order = array_reverse( $ordered_class_ids );
164 -
165 - $styles = [];
166 -
167 - foreach ( $reversed_order as $class_id ) {
168 - $item = $items[ $class_id ] ?? null;
169 -
170 - if ( ! $item ) {
171 - continue;
172 - }
173 -
174 - $resolved_label = $global_order[ $class_id ] ?? $item['label'];
175 - $item['id'] = $resolved_label;
176 - $item['label'] = $resolved_label;
177 - $styles[] = $item;
178 - }
179 -
180 - return $styles;
181 - }
182 -
183 - /**
184 - * Persist the parent-to-child and child-to-parent relation maps.
185 - *
186 - * @param array<int,int[]> $parent_to_embedded Forward map: parent_id => child_ids[].
187 - */
188 - private function persist_relation_maps( array $parent_to_embedded, string $context ): void {
189 - $cache_validity = new Cache_Validity();
190 -
191 - foreach ( $parent_to_embedded as $parent => $new_children ) {
192 - $forward_path = [ $this->get_cache_root_key( self::RELATED_KEY ), $parent, $context ];
193 -
194 - $old_related_posts = array_map( 'intval', (array) ( $cache_validity->get_meta( $forward_path ) ?? [] ) );
195 - $new_related_posts = array_map( 'intval', $new_children );
196 -
197 - $added_posts = array_diff( $new_related_posts, $old_related_posts );
198 - $removed_posts = array_diff( $old_related_posts, $new_related_posts );
199 -
200 - $cache_validity->validate( $forward_path, $new_related_posts );
201 -
202 - foreach ( $added_posts as $added_post ) {
203 - $this->add_reverse_relation( $cache_validity, $added_post, $parent, $context );
204 - }
205 - foreach ( $removed_posts as $removed_post ) {
206 - $this->remove_reverse_relation( $cache_validity, $removed_post, $parent, $context );
207 - }
208 - }
209 - }
210 -
211 - private function add_reverse_relation( Cache_Validity $cache_validity, int $child, int $parent, string $context ): void {
212 - $reverse_path = [ $this->get_cache_root_key( self::RELATED_REVERSE_KEY ), $child, $context ];
213 -
214 - $existing = array_map( 'intval', (array) ( $cache_validity->get_meta( $reverse_path ) ?? [] ) );
215 -
216 - if ( in_array( $parent, $existing, true ) ) {
217 - return;
218 - }
219 -
220 - $cache_validity->validate( $reverse_path, array_values( array_merge( $existing, [ $parent ] ) ) );
221 - }
222 -
223 - private function remove_reverse_relation( Cache_Validity $cache_validity, int $child, int $parent, string $context ): void {
224 - $reverse_path = [ $this->get_cache_root_key( self::RELATED_REVERSE_KEY ), $child, $context ];
225 -
226 - $existing = array_map( 'intval', (array) ( $cache_validity->get_meta( $reverse_path ) ?? [] ) );
227 - $pruned = array_values( array_filter( $existing, fn( int $p ) => $p !== $parent ) );
228 -
229 - $cache_validity->validate( $reverse_path, $pruned );
230 - }
231 -
232 - /**
233 - * Look up all parent post ids that declared $child_post_id as embedded.
234 - *
235 - * @param int $child_post_id
236 - * @return int[]
237 - */
238 - private function get_parent_post_ids( int $child_post_id, string $context ): array {
239 - $cache_validity = new Cache_Validity();
240 -
241 - $parents = $cache_validity->get_meta( [
242 - $this->get_cache_root_key( self::RELATED_REVERSE_KEY ),
243 - $child_post_id,
244 - $context,
245 - ] );
246 -
247 - if ( ! is_array( $parents ) ) {
248 - return [];
249 - }
250 -
251 - return array_values( array_unique( array_map( 'intval', $parents ) ) );
252 - }
253 -
254 - /**
255 - * Walk the reverse relation map transitively to collect every ancestor
256 - * post that embeds $post_id (direct parent, grandparent, etc.).
257 - *
258 - * @param int $post_id
259 - * @return int[]
260 - */
261 - private function get_ancestor_post_ids( int $post_id, string $context ): array {
262 - $ancestors = [];
263 - $visited = [];
264 - $queue = [ $post_id ];
265 -
266 - while ( ! empty( $queue ) ) {
267 - $current = array_shift( $queue );
268 -
269 - foreach ( $this->get_parent_post_ids( $current, $context ) as $parent_id ) {
270 - if ( isset( $visited[ $parent_id ] ) ) {
271 - continue;
272 - }
273 -
274 - $visited[ $parent_id ] = true;
275 - $ancestors[] = $parent_id;
276 - $queue[] = $parent_id;
277 - }
278 - }
279 -
280 - return array_values( array_unique( array_map( 'intval', $ancestors ) ) );
281 - }
282 -
283 - private function on_kit_delete( $post_id ) {
54 + private function on_post_delete( $post_id ) {
284 55 if ( ! Plugin::$instance->kits_manager->is_kit( $post_id ) ) {
285 56 return;
286 57 }
287 58
288 - $this->invalidate_all_cache();
59 + $this->invalidate_cache();
289 60 }
290 61
291 - /**
292 - * When an embedded post (component/template) is saved, its own CSS cache
293 - * is cleared by Global_Classes_Relations. We additionally need to clear
294 - * the parent's global CSS so it is regenerated with the updated child content.
295 - */
296 - private function on_document_save( $document ): void {
297 - $post_id = (int) $document->get_main_id();
298 - $context = $this->get_context();
299 -
62 + private function invalidate_cache( ?string $context = null ) {
300 63 $cache_validity = new Cache_Validity();
301 - $cache_validity->invalidate( [ $this->get_cache_root_key( self::RELATED_KEY ), $post_id, $context ] );
302 64
303 - foreach ( $this->get_ancestor_post_ids( $post_id, $context ) as $ancestor_id ) {
304 - $this->invalidate_document_cache( $ancestor_id, $context );
305 -
306 - $cache_validity->invalidate( [ $this->get_cache_root_key( self::RELATED_KEY ), $ancestor_id, $context ] );
307 - }
308 - }
309 -
310 - private function invalidate_cache_for_updated_classes( string $context, array $changes ) {
311 - if ( isset( $changes['order'] ) && $changes['order'] ) {
312 - $this->invalidate_all_cache( $context );
313 -
314 - return;
315 - }
316 -
317 - $affected = array_unique( array_merge(
318 - $changes['added'] ?? [],
319 - $changes['deleted'] ?? [],
320 - $changes['modified'] ?? []
321 - ) );
322 -
323 - if ( empty( $affected ) ) {
324 - return;
325 - }
326 -
327 - $document_ids = [];
328 - $is_preview = Global_Classes_Repository::CONTEXT_PREVIEW === $context;
329 -
330 - if ( ! empty( $changes['affected_post_ids'] ) ) {
331 - foreach ( $changes['affected_post_ids'] as $post_id ) {
332 - $document_ids[ (int) $post_id ] = true;
333 - }
334 - }
335 -
336 - foreach ( $affected as $class_id ) {
337 - foreach ( $this->relations->set_preview( $is_preview )->get_posts_by_style( $class_id ) as $doc_id ) {
338 - $document_ids[ $doc_id ] = true;
339 - }
340 - }
341 -
342 - if ( empty( $document_ids ) ) {
343 - return;
344 - }
345 -
346 - // Also include ancestor posts of the directly-affected documents so that
347 - // aggregated CSS bundles are regenerated when a descendant class changes.
348 - $with_parents = $document_ids;
349 - foreach ( array_keys( $document_ids ) as $doc_id ) {
350 - foreach ( $this->get_ancestor_post_ids( (int) $doc_id, $context ) as $ancestor_id ) {
351 - $with_parents[ $ancestor_id ] = true;
352 - }
353 - }
354 -
355 - foreach ( array_keys( $with_parents ) as $post_id ) {
356 - $this->invalidate_document_cache( $post_id, $context );
357 - }
358 - }
359 -
360 - private function invalidate_document_cache( int $post_id, ?string $context = null ) {
361 - if ( empty( $context ) ) {
362 - do_action( 'elementor/atomic-widgets/styles/clear', [ $this->get_cache_root_key(), $post_id ] );
363 - do_action( 'elementor/atomic-widgets/styles/clear', [ $this->get_cache_root_key( self::RELATED_KEY ), $post_id ] );
364 - do_action( 'elementor/atomic-widgets/styles/clear', [ $this->get_cache_root_key( self::RELATED_REVERSE_KEY ), $post_id ] );
365 - } else {
366 - do_action( 'elementor/atomic-widgets/styles/clear', [ $this->get_cache_root_key(), $post_id, $context ] );
367 - do_action( 'elementor/atomic-widgets/styles/clear', [ $this->get_cache_root_key( self::RELATED_KEY ), $post_id, $context ] );
368 - do_action( 'elementor/atomic-widgets/styles/clear', [ $this->get_cache_root_key( self::RELATED_REVERSE_KEY ), $post_id, $context ] );
369 - }
370 - }
371 -
372 - private function invalidate_all_cache( ?string $context = null ) {
373 65 if ( empty( $context ) || Global_Classes_Repository::CONTEXT_FRONTEND === $context ) {
374 - do_action( 'elementor/atomic-widgets/styles/clear', [ $this->get_cache_root_key() ] );
66 + $cache_validity->invalidate( [ self::STYLES_KEY ] );
375 67
376 - $cache_validity = new Cache_Validity();
377 - $cache_validity->invalidate( [ $this->get_cache_root_key( self::RELATED_KEY ) ] );
378 - $cache_validity->invalidate( [ $this->get_cache_root_key( self::RELATED_REVERSE_KEY ) ] );
379 -
380 68 return;
381 69 }
382 70
383 - do_action( 'elementor/atomic-widgets/styles/clear', [ $this->get_cache_root_key(), $context ] );
71 + $cache_validity->invalidate( [ self::STYLES_KEY, $context ] );
384 72 }
385 73
386 74 private function transform_classes_names( $ids ) {
75 + $context = is_preview() ? Global_Classes_Repository::CONTEXT_PREVIEW : Global_Classes_Repository::CONTEXT_FRONTEND;
387 76
388 - $labels = Global_Classes_Repository::make()
389 - ->set_preview( $this->is_preview() )
390 - ->all_labels();
77 + $classes = Global_Classes_Repository::make()
78 + ->context( $context )
79 + ->all()
80 + ->get_items();
391 81
392 82 return array_map(
393 - static function( $id ) use ( $labels ) {
394 - return $labels[ $id ] ?? $id;
83 + function( $id ) use ( $classes ) {
84 + $class = $classes->get( $id );
85 +
86 + return $class ? $class['label'] : $id;
395 87 },
396 88 $ids
397 89 );
398 - }
399 -
400 - private function get_context(): string {
401 - return $this->is_preview()
402 - ? Global_Classes_Repository::CONTEXT_PREVIEW
403 - : Global_Classes_Repository::CONTEXT_FRONTEND;
404 - }
405 -
406 - private function is_preview(): bool {
407 - return Plugin::$instance->preview->is_editor_or_preview();
408 - }
409 -
410 - private function get_cache_root_key( string $key = null ): string {
411 - return $key ? self::STYLES_KEY . '_' . $key : self::STYLES_KEY;
412 90 }
413 91 }