PluginProbe
Elementor Website Builder – more than just a page builder / 3.34.1
Elementor Website Builder – more than just a page builder v3.34.1
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/atomic-widgets/cache-validity/cache-validity-item.php +274 -6 4.2.33.34.1 View file →
@@ -1,15 +1,283 @@
1 1 <?php
2 2
3 3 namespace Elementor\Modules\AtomicWidgets\CacheValidity;
4 4
5 -use Elementor\Modules\AtomicWidgets\Styles\CacheValidity\Cache_Validity_Item as New_Cache_Validity_Item;
5 +use Elementor\Utils;
6 6
7 7 if ( ! defined( 'ABSPATH' ) ) {
8 8 exit; // Exit if accessed directly.
9 9 }
10 10
11 -// TODO: Remove this class after 3.37 is released.
12 -/**
13 - * @deprecated 3.35 Use \Elementor\Modules\AtomicWidgets\Styles\CacheValidity\Cache_Validity_Item instead.
14 - */
15 -class Cache_Validity_Item extends New_Cache_Validity_Item {}
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 +}