PluginProbe ʕ •ᴥ•ʔ
Auto Post Cleaner / 3.2.4
Auto Post Cleaner v3.2.4
3.12.0 3.13.1 3.2.4 3.2.5 3.3.0 3.3.10 3.3.11 3.3.8 3.4.2 3.5.3 3.6.0 3.7.0 3.7.1 3.7.2 3.7.3 3.7.5 3.7.6 3.8.0 3.9.0 3.9.4 3.9.6 3.9.7 trunk 3.0.0 3.1.0 3.10.1 3.10.2 3.11.4
delete-old-posts-programmatically / freemius / includes / managers / class-fs-option-manager.php
delete-old-posts-programmatically / freemius / includes / managers Last commit date
class-fs-admin-menu-manager.php 5 years ago class-fs-admin-notice-manager.php 4 years ago class-fs-cache-manager.php 5 years ago class-fs-gdpr-manager.php 5 years ago class-fs-key-value-storage.php 3 years ago class-fs-license-manager.php 5 years ago class-fs-option-manager.php 5 years ago class-fs-plan-manager.php 5 years ago class-fs-plugin-manager.php 5 years ago index.php 5 years ago
class-fs-option-manager.php
522 lines
1 <?php
2 /**
3 * @package Freemius
4 * @copyright Copyright (c) 2015, Freemius, Inc.
5 * @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
6 * @since 1.0.3
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * 3-layer lazy options manager.
15 * layer 3: Memory
16 * layer 2: Cache (if there's any caching plugin and if WP_FS__DEBUG_SDK is FALSE)
17 * layer 1: Database (options table). All options stored as one option record in the DB to reduce number of DB
18 * queries.
19 *
20 * If load() is not explicitly called, starts as empty manager. Same thing about saving the data - you have to
21 * explicitly call store().
22 *
23 * Class Freemius_Option_Manager
24 */
25 class FS_Option_Manager {
26 /**
27 * @var string
28 */
29 private $_id;
30 /**
31 * @var array|object
32 */
33 private $_options;
34 /**
35 * @var FS_Logger
36 */
37 private $_logger;
38
39 /**
40 * @since 2.0.0
41 * @var int The ID of the blog that is associated with the current site level options.
42 */
43 private $_blog_id = 0;
44
45 /**
46 * @since 2.0.0
47 * @var bool
48 */
49 private $_is_network_storage;
50
51 /**
52 * @var bool|null
53 */
54 private $_autoload;
55
56 /**
57 * @var array[string]FS_Option_Manager {
58 * @key string
59 * @value FS_Option_Manager
60 * }
61 */
62 private static $_MANAGERS = array();
63
64 /**
65 * @author Vova Feldman (@svovaf)
66 * @since 1.0.3
67 *
68 * @param string $id
69 * @param bool $load
70 * @param bool|int $network_level_or_blog_id Since 2.0.0
71 * @param bool|null $autoload
72 */
73 private function __construct(
74 $id,
75 $load = false,
76 $network_level_or_blog_id = false,
77 $autoload = null
78 ) {
79 $id = strtolower( $id );
80
81 $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_opt_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
82
83 $this->_logger->entrance();
84 $this->_logger->log( 'id = ' . $id );
85
86 $this->_id = $id;
87
88 $this->_autoload = $autoload;
89
90 if ( is_multisite() ) {
91 $this->_is_network_storage = ( true === $network_level_or_blog_id );
92
93 if ( is_numeric( $network_level_or_blog_id ) ) {
94 $this->_blog_id = $network_level_or_blog_id;
95 }
96 } else {
97 $this->_is_network_storage = false;
98 }
99
100 if ( $load ) {
101 $this->load();
102 }
103 }
104
105 /**
106 * @author Vova Feldman (@svovaf)
107 * @since 1.0.3
108 *
109 * @param string $id
110 * @param bool $load
111 * @param bool|int $network_level_or_blog_id Since 2.0.0
112 * @param bool|null $autoload
113 *
114 * @return \FS_Option_Manager
115 */
116 static function get_manager(
117 $id,
118 $load = false,
119 $network_level_or_blog_id = false,
120 $autoload = null
121 ) {
122 $key = strtolower( $id );
123
124 if ( is_multisite() ) {
125 if ( true === $network_level_or_blog_id ) {
126 $key .= ':ms';
127 } else if ( is_numeric( $network_level_or_blog_id ) && $network_level_or_blog_id > 0 ) {
128 $key .= ":{$network_level_or_blog_id}";
129 } else {
130 $network_level_or_blog_id = get_current_blog_id();
131
132 $key .= ":{$network_level_or_blog_id}";
133 }
134 }
135
136 if ( ! isset( self::$_MANAGERS[ $key ] ) ) {
137 self::$_MANAGERS[ $key ] = new FS_Option_Manager(
138 $id,
139 $load,
140 $network_level_or_blog_id,
141 $autoload
142 );
143 } // If load required but not yet loaded, load.
144 else if ( $load && ! self::$_MANAGERS[ $key ]->is_loaded() ) {
145 self::$_MANAGERS[ $key ]->load();
146 }
147
148 return self::$_MANAGERS[ $key ];
149 }
150
151 /**
152 * @author Vova Feldman (@svovaf)
153 * @since 1.0.3
154 *
155 * @param bool $flush
156 */
157 function load( $flush = false ) {
158 $this->_logger->entrance();
159
160 $option_name = $this->get_option_manager_name();
161
162 if ( $flush || ! isset( $this->_options ) ) {
163 if ( isset( $this->_options ) ) {
164 // Clear prev options.
165 $this->clear();
166 }
167
168 $cache_group = $this->get_cache_group();
169
170 if ( WP_FS__DEBUG_SDK ) {
171
172 // Don't use cache layer in DEBUG mode.
173 $load_options = empty( $this->_options );
174
175 } else {
176
177 $this->_options = wp_cache_get(
178 $option_name,
179 $cache_group
180 );
181
182 $load_options = ( false === $this->_options );
183 }
184
185 $cached = true;
186
187 if ( $load_options ) {
188 if ( $this->_is_network_storage ) {
189 $this->_options = get_site_option( $option_name );
190 } else if ( $this->_blog_id > 0 ) {
191 $this->_options = get_blog_option( $this->_blog_id, $option_name );
192 } else {
193 $this->_options = get_option( $option_name );
194 }
195
196 if ( is_string( $this->_options ) ) {
197 $this->_options = json_decode( $this->_options );
198 }
199
200 // $this->_logger->info('get_option = ' . var_export($this->_options, true));
201
202 if ( false === $this->_options ) {
203 $this->clear();
204 }
205
206 $cached = false;
207 }
208
209 if ( ! WP_FS__DEBUG_SDK && ! $cached ) {
210 // Set non encoded cache.
211 wp_cache_set( $option_name, $this->_options, $cache_group );
212 }
213 }
214 }
215
216 /**
217 * @author Vova Feldman (@svovaf)
218 * @since 1.0.3
219 *
220 * @return bool
221 */
222 function is_loaded() {
223 return isset( $this->_options );
224 }
225
226 /**
227 * @author Vova Feldman (@svovaf)
228 * @since 1.0.3
229 *
230 * @return bool
231 */
232 function is_empty() {
233 return ( $this->is_loaded() && false === $this->_options );
234 }
235
236 /**
237 * @author Vova Feldman (@svovaf)
238 * @since 1.0.6
239 *
240 * @param bool $flush
241 */
242 function clear( $flush = false ) {
243 $this->_logger->entrance();
244
245 $this->_options = array();
246
247 if ( $flush ) {
248 $this->store();
249 }
250 }
251
252 /**
253 * Delete options manager from DB.
254 *
255 * @author Vova Feldman (@svovaf)
256 * @since 1.0.9
257 */
258 function delete() {
259 $option_name = $this->get_option_manager_name();
260
261 if ( $this->_is_network_storage ) {
262 delete_site_option( $option_name );
263 } else if ( $this->_blog_id > 0 ) {
264 delete_blog_option( $this->_blog_id, $option_name );
265 } else {
266 delete_option( $option_name );
267 }
268 }
269
270 /**
271 * @author Vova Feldman (@svovaf)
272 * @since 1.0.6
273 *
274 * @param string $option
275 *
276 * @return bool
277 */
278 function has_option( $option ) {
279 return array_key_exists( $option, $this->_options );
280 }
281
282 /**
283 * @author Vova Feldman (@svovaf)
284 * @since 1.0.3
285 *
286 * @param string $option
287 * @param mixed $default
288 *
289 * @return mixed
290 */
291 function get_option( $option, $default = null ) {
292 $this->_logger->entrance( 'option = ' . $option );
293
294 if ( ! $this->is_loaded() ) {
295 $this->load();
296 }
297
298 if ( is_array( $this->_options ) ) {
299 $value = isset( $this->_options[ $option ] ) ?
300 $this->_options[ $option ] :
301 $default;
302 } else if ( is_object( $this->_options ) ) {
303 $value = isset( $this->_options->{$option} ) ?
304 $this->_options->{$option} :
305 $default;
306 } else {
307 $value = $default;
308 }
309
310 /**
311 * If it's an object, return a clone of the object, otherwise,
312 * external changes of the object will actually change the value
313 * of the object in the options manager which may lead to an unexpected
314 * behaviour and data integrity when a store() call is triggered.
315 *
316 * Example:
317 * $object1 = $options->get_option( 'object1' );
318 * $object1->x = 123;
319 *
320 * $object2 = $options->get_option( 'object2' );
321 * $object2->y = 'dummy';
322 *
323 * $options->set_option( 'object2', $object2, true );
324 *
325 * If we don't return a clone of option 'object1', setting 'object2'
326 * will also store the updated value of 'object1' which is quite not
327 * an expected behaviour.
328 *
329 * @author Vova Feldman
330 */
331 return is_object( $value ) ? clone $value : $value;
332 }
333
334 /**
335 * @author Vova Feldman (@svovaf)
336 * @since 1.0.3
337 *
338 * @param string $option
339 * @param mixed $value
340 * @param bool $flush
341 */
342 function set_option( $option, $value, $flush = false ) {
343 $this->_logger->entrance( 'option = ' . $option );
344
345 if ( ! $this->is_loaded() ) {
346 $this->clear();
347 }
348
349 /**
350 * If it's an object, store a clone of the object, otherwise,
351 * external changes of the object will actually change the value
352 * of the object in the options manager which may lead to an unexpected
353 * behaviour and data integrity when a store() call is triggered.
354 *
355 * Example:
356 * $object1 = new stdClass();
357 * $object1->x = 123;
358 *
359 * $options->set_option( 'object1', $object1 );
360 *
361 * $object1->x = 456;
362 *
363 * $options->set_option( 'object2', $object2, true );
364 *
365 * If we don't set the option as a clone of option 'object1', setting 'object2'
366 * will also store the updated value of 'object1' ($object1->x = 456 instead of
367 * $object1->x = 123) which is quite not an expected behaviour.
368 *
369 * @author Vova Feldman
370 */
371 $copy = is_object( $value ) ? clone $value : $value;
372
373 if ( is_array( $this->_options ) ) {
374 $this->_options[ $option ] = $copy;
375 } else if ( is_object( $this->_options ) ) {
376 $this->_options->{$option} = $copy;
377 }
378
379 if ( $flush ) {
380 $this->store();
381 }
382 }
383
384 /**
385 * Unset option.
386 *
387 * @author Vova Feldman (@svovaf)
388 * @since 1.0.3
389 *
390 * @param string $option
391 * @param bool $flush
392 */
393 function unset_option( $option, $flush = false ) {
394 $this->_logger->entrance( 'option = ' . $option );
395
396 if ( is_array( $this->_options ) ) {
397 if ( ! isset( $this->_options[ $option ] ) ) {
398 return;
399 }
400
401 unset( $this->_options[ $option ] );
402
403 } else if ( is_object( $this->_options ) ) {
404 if ( ! isset( $this->_options->{$option} ) ) {
405 return;
406 }
407
408 unset( $this->_options->{$option} );
409 }
410
411 if ( $flush ) {
412 $this->store();
413 }
414 }
415
416 /**
417 * Dump options to database.
418 *
419 * @author Vova Feldman (@svovaf)
420 * @since 1.0.3
421 */
422 function store() {
423 $this->_logger->entrance();
424
425 $option_name = $this->get_option_manager_name();
426
427 if ( $this->_logger->is_on() ) {
428 $this->_logger->info( $option_name . ' = ' . var_export( $this->_options, true ) );
429 }
430
431 // Update DB.
432 if ( $this->_is_network_storage ) {
433 update_site_option( $option_name, $this->_options );
434 } else if ( $this->_blog_id > 0 ) {
435 update_blog_option( $this->_blog_id, $option_name, $this->_options );
436 } else {
437 update_option( $option_name, $this->_options, $this->_autoload );
438 }
439
440 if ( ! WP_FS__DEBUG_SDK ) {
441 wp_cache_set( $option_name, $this->_options, $this->get_cache_group() );
442 }
443 }
444
445 /**
446 * Get options keys.
447 *
448 * @author Vova Feldman (@svovaf)
449 * @since 1.0.3
450 *
451 * @return string[]
452 */
453 function get_options_keys() {
454 if ( is_array( $this->_options ) ) {
455 return array_keys( $this->_options );
456 } else if ( is_object( $this->_options ) ) {
457 return array_keys( get_object_vars( $this->_options ) );
458 }
459
460 return array();
461 }
462
463 #--------------------------------------------------------------------------------
464 #region Migration
465 #--------------------------------------------------------------------------------
466
467 /**
468 * Migrate options from site level.
469 *
470 * @author Vova Feldman (@svovaf)
471 * @since 2.0.0
472 */
473 function migrate_to_network() {
474 $site_options = FS_Option_Manager::get_manager($this->_id, true, false);
475
476 $options = is_object( $site_options->_options ) ?
477 get_object_vars( $site_options->_options ) :
478 $site_options->_options;
479
480 if ( ! empty( $options ) ) {
481 foreach ( $options as $key => $val ) {
482 $this->set_option( $key, $val, false );
483 }
484
485 $this->store();
486 }
487 }
488
489 #endregion
490
491 #--------------------------------------------------------------------------------
492 #region Helper Methods
493 #--------------------------------------------------------------------------------
494
495 /**
496 * @return string
497 */
498 private function get_option_manager_name() {
499 return $this->_id;
500 }
501
502 /**
503 * @author Vova Feldman (@svovaf)
504 * @since 2.0.0
505 *
506 * @return string
507 */
508 private function get_cache_group() {
509 $group = WP_FS__SLUG;
510
511 if ( $this->_is_network_storage ) {
512 $group .= '_ms';
513 } else if ( $this->_blog_id > 0 ) {
514 $group .= "_s{$this->_blog_id}";
515 }
516
517 return $group;
518 }
519
520 #endregion
521 }
522