PluginProbe
Elementor Website Builder – more than just a page builder / 3.35.8
Elementor Website Builder – more than just a page builder v3.35.8
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 4.0.7 All 451 releases
elementor / modules / atomic-widgets / styles / cache-validity / cache-validity-item.php

cache-validity-item.php in Elementor Website Builder – more than just a page builder 3.35.8, at modules/atomic-widgets/styles/cache-validity/cache-validity-item.php

284 lines 7.9 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\CacheValidity;
4
5 use Elementor\Utils;
6
7 if ( ! defined( 'ABSPATH' ) ) {
8 exit; // Exit if accessed directly.
9 }
10
11
12 class Cache_Validity_Item {
13 const CACHE_KEY_PREFIX = 'elementor_atomic_cache_validity__';
14
15 private string $root;
16
17 public function __construct( string $root ) {
18 $this->root = $root;
19 }
20
21 public function get( array $keys ): ?array {
22 return $this->wrap_exception( function() use ( $keys ) {
23 $data = $this->get_stored_data();
24
25 $node = $this->get_node( $data, $keys );
26
27 if ( null === $node ) {
28 return null;
29 }
30
31 return is_bool( $node ) ? [ 'state' => $node ] : $node;
32 } );
33 }
34
35 public function validate( array $keys, $meta = null ) {
36 return $this->wrap_exception( function() use ( $keys, $meta ) {
37 $data = $this->get_stored_data();
38
39 if ( empty( $keys ) ) {
40 $data['state'] = true;
41 $data['meta'] = $meta;
42
43 $this->update_stored_data( $data );
44
45 return;
46 }
47
48 $this->validate_nested_node( $data, $keys, $meta );
49 } );
50 }
51
52 public function invalidate( array $keys ) {
53 return $this->wrap_exception( function() use ( $keys ) {
54 if ( empty( $keys ) ) {
55 $this->delete_stored_data();
56
57 return;
58 }
59
60 $data = $this->get_stored_data();
61
62 $this->invalidate_nested_node( $data, $keys );
63 } );
64 }
65
66 /**
67 * @param array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} | boolean $data
68 * @param array<string> $keys
69 * @param mixed | null $meta
70 */
71 private function validate_nested_node( array $data, array $keys, $meta = null ) {
72 $data = $this->ensure_path( $data, $keys );
73
74 $last_key = array_pop( $keys );
75
76 // parent is guaranteed to be an array as we send the full $keys array to ensure_path
77 $parent = &$this->get_node( $data, $keys );
78
79 $old_node = &$parent['children'][ $last_key ];
80
81 $has_children = is_array( $old_node ) && ! empty( $old_node['children'] );
82
83 if ( null === $meta && ! $has_children ) {
84 $parent['children'][ $last_key ] = true;
85
86 $this->update_stored_data( $data );
87 return;
88 }
89
90 $new_node = [
91 'state' => true,
92 ];
93
94 if ( $has_children ) {
95 $new_node['children'] = $old_node['children'];
96 }
97
98 if ( null !== $meta ) {
99 $new_node['meta'] = $meta;
100 }
101
102 $parent['children'][ $last_key ] = $new_node;
103
104 $this->update_stored_data( $data );
105 }
106
107 /**
108 * @param array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} | boolean $data
109 * @param array<string> $keys
110 */
111 private function invalidate_nested_node( array $data, array $keys ) {
112 $last_key = array_pop( $keys );
113 $parent = &$this->get_node( $data, $keys );
114
115 if ( ! is_array( $parent ) || ! isset( $parent['children'][ $last_key ] ) ) {
116 // node doesn't exist - no need to do anything
117 return;
118 }
119
120 if ( count( $parent['children'] ) === 1 ) {
121 // if the invalidated node is the parent's o nly child - normalize the data
122 $data = $this->get_normalized_data( $data, $keys, $last_key );
123
124 $this->update_stored_data( $data );
125 return;
126 }
127
128 unset( $parent['children'][ $last_key ] );
129
130 $this->update_stored_data( $data );
131 }
132
133 /**
134 * @param array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} $data
135 * @param array<string> $keys
136 * @param string $last_key
137 * @return array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>}
138 */
139 private function get_normalized_data( array $data, array $keys, string $last_key ) {
140 $obsolete_root_params = &$this->find_empty_parents_path_root( $data, $keys, $last_key );
141 $parent = &$this->get_node( $data, $keys );
142
143 if ( $obsolete_root_params['node'] && $obsolete_root_params['key'] ) {
144 unset( $obsolete_root_params['node']['children'][ $obsolete_root_params['key'] ] );
145
146 return $data;
147 }
148
149 if ( $obsolete_root_params['node'] ) {
150 unset( $data['children'] );
151
152 return $data;
153 }
154
155 if ( $parent ) {
156 unset( $parent['children'][ $last_key ] );
157 }
158
159 return $data;
160 }
161
162 /**
163 * @param array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} | boolean $data
164 * @param array<string> $keys
165 * @return array{key: string | null, node: array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} | null}
166 */
167 private function &find_empty_parents_path_root( array &$data, array $keys ) {
168 $root_node = [
169 'key' => null,
170 'node' => null,
171 ];
172
173 $current = &$data;
174 $parent = &$current;
175
176 while ( ! empty( $keys ) ) {
177 $key = array_shift( $keys );
178 $parent = &$current;
179 $current = &$current['children'][ $key ];
180
181 if ( $this->is_empty_parent( $current ) && empty( $root_node['node'] ) ) {
182 $root_node = [
183 'key' => $key,
184 'node' => &$parent,
185 ];
186 } elseif ( is_array( $current ) && ! $this->is_empty_parent( $current ) ) {
187 $root_node = [
188 'key' => null,
189 'node' => null,
190 ];
191 }
192 }
193
194 return $root_node;
195 }
196
197 /**
198 * Retrieves the stored tree, guaranteed to have a path representation based on $keys
199 *
200 * @param array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} | boolean $data
201 * @param array<string> $keys
202 * @return array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>}
203 */
204 private function ensure_path( array $data, array $keys ): ?array {
205 $current = &$data;
206
207 while ( ! empty( $keys ) ) {
208 $key = array_shift( $keys );
209
210 if ( is_bool( $current ) ) {
211 $current = [ 'state' => $current ];
212 }
213
214 if ( ! isset( $current['children'] ) ) {
215 $current['children'] = [];
216 }
217
218 if ( ! isset( $current['children'][ $key ] ) ) {
219 $current['children'][ $key ] = [ 'state' => false ];
220 }
221
222 $current = &$current['children'][ $key ];
223 }
224
225 return $data;
226 }
227
228 /**
229 * @param array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} | boolean $data
230 * @param array<string> $keys
231 * @return array<array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} | boolean | null> | null | boolean
232 */
233 private function &get_node( array &$data, array $keys ) {
234 $current = &$data;
235
236 while ( ! empty( $keys ) ) {
237 $key = array_shift( $keys );
238
239 if ( isset( $current['children'][ $key ] ) ) {
240 $current = &$current['children'][ $key ];
241 } else {
242 $current = null;
243 }
244 }
245
246 return $current;
247 }
248
249 private function is_empty_parent( $data ): bool {
250 if ( ! is_array( $data ) ) {
251 return false;
252 }
253
254 return ( ! isset( $data['children'] ) || 1 === count( $data['children'] ) ) && ! $data['state'];
255 }
256
257 /**
258 * @return array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>}
259 */
260 private function get_stored_data() {
261 return get_option( self::CACHE_KEY_PREFIX . $this->root, [ 'state' => false ] );
262 }
263
264 /**
265 * @param array{state: boolean, meta: array<string, mixed> | null, children: array<string, self>} $data
266 */
267 private function update_stored_data( $data ) {
268 // setting autoload with false to avoid unnecessary memory usage
269 update_option( self::CACHE_KEY_PREFIX . $this->root, $data, false );
270 }
271
272 private function delete_stored_data() {
273 delete_option( self::CACHE_KEY_PREFIX . $this->root );
274 }
275
276 private function wrap_exception( callable $callback ) {
277 try {
278 return $callback();
279 } catch ( \Exception $e ) {
280 $this->delete_stored_data();
281 }
282 }
283 }
284