PluginProbe
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder / 1.5
Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder v1.5
3.6.0 3.5.0 trunk 1.0.10 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.6.1 1.0.7 1.0.8 1.0.9 1.1 1.1.1 1.2 1.3 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.5.1 All 110 releases
buttonizer-multifunctional-button / freemius / includes / managers / class-fs-option-manager.php

class-fs-option-manager.php in Buttonizer – Floating Menus, Sticky Buttons, & Popup Builder 1.5, at freemius/includes/managers/class-fs-option-manager.php

490 lines 14.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 array[string]FS_Option_Manager {
53 * @key string
54 * @value FS_Option_Manager
55 * }
56 */
57 private static $_MANAGERS = array();
58
59 /**
60 * @author Vova Feldman (@svovaf)
61 * @since 1.0.3
62 *
63 * @param string $id
64 * @param bool $load
65 * @param bool|int $network_level_or_blog_id Since 2.0.0
66 */
67 private function __construct( $id, $load = false, $network_level_or_blog_id = false ) {
68 $id = strtolower( $id );
69
70 $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_opt_mngr_' . $id, WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
71
72 $this->_logger->entrance();
73 $this->_logger->log( 'id = ' . $id );
74
75 $this->_id = $id;
76
77 if ( is_multisite() ) {
78 $this->_is_network_storage = ( true === $network_level_or_blog_id );
79
80 if ( is_numeric( $network_level_or_blog_id ) ) {
81 $this->_blog_id = $network_level_or_blog_id;
82 }
83 } else {
84 $this->_is_network_storage = false;
85 }
86
87 if ( $load ) {
88 $this->load();
89 }
90 }
91
92 /**
93 * @author Vova Feldman (@svovaf)
94 * @since 1.0.3
95 *
96 * @param string $id
97 * @param bool $load
98 * @param bool|int $network_level_or_blog_id Since 2.0.0
99 *
100 * @return FS_Option_Manager
101 */
102 static function get_manager( $id, $load = false, $network_level_or_blog_id = false ) {
103 $key = strtolower( $id );
104
105 if ( is_multisite() ) {
106 if ( true === $network_level_or_blog_id ) {
107 $key .= ':ms';
108 } else if ( is_numeric( $network_level_or_blog_id ) && $network_level_or_blog_id > 0 ) {
109 $key .= ":{$network_level_or_blog_id}";
110 } else {
111 $network_level_or_blog_id = get_current_blog_id();
112
113 $key .= ":{$network_level_or_blog_id}";
114 }
115 }
116
117 if ( ! isset( self::$_MANAGERS[ $key ] ) ) {
118 self::$_MANAGERS[ $key ] = new FS_Option_Manager( $id, $load, $network_level_or_blog_id );
119 } // If load required but not yet loaded, load.
120 else if ( $load && ! self::$_MANAGERS[ $key ]->is_loaded() ) {
121 self::$_MANAGERS[ $key ]->load();
122 }
123
124 return self::$_MANAGERS[ $key ];
125 }
126
127 /**
128 * @author Vova Feldman (@svovaf)
129 * @since 1.0.3
130 *
131 * @param bool $flush
132 */
133 function load( $flush = false ) {
134 $this->_logger->entrance();
135
136 $option_name = $this->get_option_manager_name();
137
138 if ( $flush || ! isset( $this->_options ) ) {
139 if ( isset( $this->_options ) ) {
140 // Clear prev options.
141 $this->clear();
142 }
143
144 $cache_group = $this->get_cache_group();
145
146 if ( ! WP_FS__DEBUG_SDK ) {
147
148 $this->_options = wp_cache_get(
149 $option_name,
150 $cache_group
151 );
152 }
153
154 $cached = true;
155
156 if ( empty( $this->_options ) ) {
157 if ( $this->_is_network_storage ) {
158 $this->_options = get_site_option( $option_name );
159 } else if ( $this->_blog_id > 0 ) {
160 $this->_options = get_blog_option( $this->_blog_id, $option_name );
161 } else {
162 $this->_options = get_option( $option_name );
163 }
164
165 if ( is_string( $this->_options ) ) {
166 $this->_options = json_decode( $this->_options );
167 }
168
169 // $this->_logger->info('get_option = ' . var_export($this->_options, true));
170
171 if ( false === $this->_options ) {
172 $this->clear();
173 }
174
175 $cached = false;
176 }
177
178 if ( ! WP_FS__DEBUG_SDK && ! $cached ) {
179 // Set non encoded cache.
180 wp_cache_set( $option_name, $this->_options, $cache_group );
181 }
182 }
183 }
184
185 /**
186 * @author Vova Feldman (@svovaf)
187 * @since 1.0.3
188 *
189 * @return bool
190 */
191 function is_loaded() {
192 return isset( $this->_options );
193 }
194
195 /**
196 * @author Vova Feldman (@svovaf)
197 * @since 1.0.3
198 *
199 * @return bool
200 */
201 function is_empty() {
202 return ( $this->is_loaded() && false === $this->_options );
203 }
204
205 /**
206 * @author Vova Feldman (@svovaf)
207 * @since 1.0.6
208 *
209 * @param bool $flush
210 */
211 function clear( $flush = false ) {
212 $this->_logger->entrance();
213
214 $this->_options = array();
215
216 if ( $flush ) {
217 $this->store();
218 }
219 }
220
221 /**
222 * Delete options manager from DB.
223 *
224 * @author Vova Feldman (@svovaf)
225 * @since 1.0.9
226 */
227 function delete() {
228 $option_name = $this->get_option_manager_name();
229
230 if ( $this->_is_network_storage ) {
231 delete_site_option( $option_name );
232 } else if ( $this->_blog_id > 0 ) {
233 delete_blog_option( $this->_blog_id, $option_name );
234 } else {
235 delete_option( $option_name );
236 }
237 }
238
239 /**
240 * @author Vova Feldman (@svovaf)
241 * @since 1.0.6
242 *
243 * @param string $option
244 *
245 * @return bool
246 */
247 function has_option( $option ) {
248 return array_key_exists( $option, $this->_options );
249 }
250
251 /**
252 * @author Vova Feldman (@svovaf)
253 * @since 1.0.3
254 *
255 * @param string $option
256 * @param mixed $default
257 *
258 * @return mixed
259 */
260 function get_option( $option, $default = null ) {
261 $this->_logger->entrance( 'option = ' . $option );
262
263 if ( ! $this->is_loaded() ) {
264 $this->load();
265 }
266
267 if ( is_array( $this->_options ) ) {
268 $value = isset( $this->_options[ $option ] ) ?
269 $this->_options[ $option ] :
270 $default;
271 } else if ( is_object( $this->_options ) ) {
272 $value = isset( $this->_options->{$option} ) ?
273 $this->_options->{$option} :
274 $default;
275 } else {
276 $value = $default;
277 }
278
279 /**
280 * If it's an object, return a clone of the object, otherwise,
281 * external changes of the object will actually change the value
282 * of the object in the options manager which may lead to an unexpected
283 * behaviour and data integrity when a store() call is triggered.
284 *
285 * Example:
286 * $object1 = $options->get_option( 'object1' );
287 * $object1->x = 123;
288 *
289 * $object2 = $options->get_option( 'object2' );
290 * $object2->y = 'dummy';
291 *
292 * $options->set_option( 'object2', $object2, true );
293 *
294 * If we don't return a clone of option 'object1', setting 'object2'
295 * will also store the updated value of 'object1' which is quite not
296 * an expected behaviour.
297 *
298 * @author Vova Feldman
299 */
300 return is_object( $value ) ? clone $value : $value;
301 }
302
303 /**
304 * @author Vova Feldman (@svovaf)
305 * @since 1.0.3
306 *
307 * @param string $option
308 * @param mixed $value
309 * @param bool $flush
310 */
311 function set_option( $option, $value, $flush = false ) {
312 $this->_logger->entrance( 'option = ' . $option );
313
314 if ( ! $this->is_loaded() ) {
315 $this->clear();
316 }
317
318 /**
319 * If it's an object, store a clone of the object, otherwise,
320 * external changes of the object will actually change the value
321 * of the object in the options manager which may lead to an unexpected
322 * behaviour and data integrity when a store() call is triggered.
323 *
324 * Example:
325 * $object1 = new stdClass();
326 * $object1->x = 123;
327 *
328 * $options->set_option( 'object1', $object1 );
329 *
330 * $object1->x = 456;
331 *
332 * $options->set_option( 'object2', $object2, true );
333 *
334 * If we don't set the option as a clone of option 'object1', setting 'object2'
335 * will also store the updated value of 'object1' ($object1->x = 456 instead of
336 * $object1->x = 123) which is quite not an expected behaviour.
337 *
338 * @author Vova Feldman
339 */
340 $copy = is_object( $value ) ? clone $value : $value;
341
342 if ( is_array( $this->_options ) ) {
343 $this->_options[ $option ] = $copy;
344 } else if ( is_object( $this->_options ) ) {
345 $this->_options->{$option} = $copy;
346 }
347
348 if ( $flush ) {
349 $this->store();
350 }
351 }
352
353 /**
354 * Unset option.
355 *
356 * @author Vova Feldman (@svovaf)
357 * @since 1.0.3
358 *
359 * @param string $option
360 * @param bool $flush
361 */
362 function unset_option( $option, $flush = false ) {
363 $this->_logger->entrance( 'option = ' . $option );
364
365 if ( is_array( $this->_options ) ) {
366 if ( ! isset( $this->_options[ $option ] ) ) {
367 return;
368 }
369
370 unset( $this->_options[ $option ] );
371
372 } else if ( is_object( $this->_options ) ) {
373 if ( ! isset( $this->_options->{$option} ) ) {
374 return;
375 }
376
377 unset( $this->_options->{$option} );
378 }
379
380 if ( $flush ) {
381 $this->store();
382 }
383 }
384
385 /**
386 * Dump options to database.
387 *
388 * @author Vova Feldman (@svovaf)
389 * @since 1.0.3
390 */
391 function store() {
392 $this->_logger->entrance();
393
394 $option_name = $this->get_option_manager_name();
395
396 if ( $this->_logger->is_on() ) {
397 $this->_logger->info( $option_name . ' = ' . var_export( $this->_options, true ) );
398 }
399
400 // Update DB.
401 if ( $this->_is_network_storage ) {
402 update_site_option( $option_name, $this->_options );
403 } else if ( $this->_blog_id > 0 ) {
404 update_blog_option( $this->_blog_id, $option_name, $this->_options );
405 } else {
406 update_option( $option_name, $this->_options );
407 }
408
409 if ( ! WP_FS__DEBUG_SDK ) {
410 wp_cache_set( $option_name, $this->_options, $this->get_cache_group() );
411 }
412 }
413
414 /**
415 * Get options keys.
416 *
417 * @author Vova Feldman (@svovaf)
418 * @since 1.0.3
419 *
420 * @return string[]
421 */
422 function get_options_keys() {
423 if ( is_array( $this->_options ) ) {
424 return array_keys( $this->_options );
425 } else if ( is_object( $this->_options ) ) {
426 return array_keys( get_object_vars( $this->_options ) );
427 }
428
429 return array();
430 }
431
432 #--------------------------------------------------------------------------------
433 #region Migration
434 #--------------------------------------------------------------------------------
435
436 /**
437 * Migrate options from site level.
438 *
439 * @author Vova Feldman (@svovaf)
440 * @since 2.0.0
441 */
442 function migrate_to_network() {
443 $site_options = FS_Option_Manager::get_manager($this->_id, true, false);
444
445 $options = is_object( $site_options->_options ) ?
446 get_object_vars( $site_options->_options ) :
447 $site_options->_options;
448
449 if ( ! empty( $options ) ) {
450 foreach ( $options as $key => $val ) {
451 $this->set_option( $key, $val, false );
452 }
453
454 $this->store();
455 }
456 }
457
458 #endregion
459
460 #--------------------------------------------------------------------------------
461 #region Helper Methods
462 #--------------------------------------------------------------------------------
463
464 /**
465 * @return string
466 */
467 private function get_option_manager_name() {
468 return $this->_id;
469 }
470
471 /**
472 * @author Vova Feldman (@svovaf)
473 * @since 2.0.0
474 *
475 * @return string
476 */
477 private function get_cache_group() {
478 $group = WP_FS__SLUG;
479
480 if ( $this->_is_network_storage ) {
481 $group .= '_ms';
482 } else if ( $this->_blog_id > 0 ) {
483 $group .= "_s{$this->_blog_id}";
484 }
485
486 return $group;
487 }
488
489 #endregion
490 }