PluginProbe ʕ •ᴥ•ʔ
Photo Gallery by FooGallery : Responsive Image Gallery, Masonry Gallery & Carousel / trunk
Photo Gallery by FooGallery : Responsive Image Gallery, Masonry Gallery & Carousel vtrunk
trunk 1.10.3 2.0.24 2.1.34 2.2.44 2.3.3 2.4.32 3.0.6 3.1.11 3.1.12 3.1.13 3.1.20 3.1.25 3.1.26 3.1.26.1 3.1.26.2
foogallery / freemius / includes / class-fs-options.php
foogallery / freemius / includes Last commit date
customizer 7 months ago debug 2 years ago entities 7 months ago managers 7 months ago sdk 2 years ago supplements 3 years ago .DS_Store 7 months ago class-freemius-abstract.php 3 years ago class-freemius.php 7 months ago class-fs-admin-notices.php 3 years ago class-fs-api.php 7 months ago class-fs-garbage-collector.php 7 months ago class-fs-hook-snapshot.php 7 months ago class-fs-lock.php 3 years ago class-fs-logger.php 7 months ago class-fs-options.php 8 years ago class-fs-plugin-updater.php 7 months ago class-fs-security.php 7 months ago class-fs-storage.php 7 months ago class-fs-user-lock.php 3 years ago fs-core-functions.php 7 months ago fs-essential-functions.php 2 years ago fs-html-escaping-functions.php 7 months ago fs-plugin-info-dialog.php 7 months ago index.php 3 years ago l10n.php 3 years ago
class-fs-options.php
431 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.2.3
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 /**
14 * Class FS_Options
15 *
16 * A wrapper class for handling network level and single site level options.
17 */
18 class FS_Options {
19 /**
20 * @var string
21 */
22 private $_id;
23
24 /**
25 * @var array[string]FS_Options {
26 * @key string
27 * @value FS_Options
28 * }
29 */
30 private static $_instances;
31
32 /**
33 * @var FS_Option_Manager Site level options.
34 */
35 private $_options;
36
37 /**
38 * @var FS_Option_Manager Network level options.
39 */
40 private $_network_options;
41
42 /**
43 * @var int The ID of the blog that is associated with the current site level options.
44 */
45 private $_blog_id = 0;
46
47 /**
48 * @var bool
49 */
50 private $_is_multisite;
51
52 /**
53 * @var string[] Lazy collection of params on the site level.
54 */
55 private static $_SITE_OPTIONS_MAP;
56
57 /**
58 * @author Leo Fajardo (@leorw)
59 * @since 2.0.0
60 *
61 * @param string $id
62 * @param bool $load
63 *
64 * @return FS_Options
65 */
66 static function instance( $id, $load = false ) {
67 if ( ! isset( self::$_instances[ $id ] ) ) {
68 self::$_instances[ $id ] = new FS_Options( $id, $load );
69 }
70
71 return self::$_instances[ $id ];
72 }
73
74 /**
75 * @author Leo Fajardo (@leorw)
76 * @since 2.0.0
77 *
78 * @param string $id
79 * @param bool $load
80 */
81 private function __construct( $id, $load = false ) {
82 $this->_id = $id;
83 $this->_is_multisite = is_multisite();
84
85 if ( $this->_is_multisite ) {
86 $this->_blog_id = get_current_blog_id();
87 $this->_network_options = FS_Option_Manager::get_manager( $id, $load, true );
88 }
89
90 $this->_options = FS_Option_Manager::get_manager( $id, $load, $this->_blog_id );
91 }
92
93 /**
94 * Switch the context of the site level options manager.
95 *
96 * @author Vova Feldman (@svovaf)
97 * @since 2.0.0
98 *
99 * @param $blog_id
100 */
101 function set_site_blog_context( $blog_id ) {
102 $this->_blog_id = $blog_id;
103
104 $this->_options = FS_Option_Manager::get_manager( $this->_id, false, $this->_blog_id );
105 }
106
107 /**
108 * @author Leo Fajardo (@leorw)
109 *
110 * @param string $option
111 * @param mixed $default
112 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
113 *
114 * @return mixed
115 */
116 function get_option( $option, $default = null, $network_level_or_blog_id = null ) {
117 if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
118 return $this->_network_options->get_option( $option, $default );
119 }
120
121 $site_options = $this->get_site_options( $network_level_or_blog_id );
122
123 return $site_options->get_option( $option, $default );
124 }
125
126 /**
127 * @author Leo Fajardo (@leorw)
128 * @since 2.0.0
129 *
130 * @param string $option
131 * @param mixed $value
132 * @param bool $flush
133 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
134 */
135 function set_option( $option, $value, $flush = false, $network_level_or_blog_id = null ) {
136 if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
137 $this->_network_options->set_option( $option, $value, $flush );
138 } else {
139 $site_options = $this->get_site_options( $network_level_or_blog_id );
140 $site_options->set_option( $option, $value, $flush );
141 }
142 }
143
144 /**
145 * @author Vova Feldman (@svovaf)
146 * @since 2.0.0
147 *
148 * @param string $option
149 * @param bool $flush
150 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
151 */
152 function unset_option( $option, $flush = false, $network_level_or_blog_id = null ) {
153 if ( $this->should_use_network_storage( $option, $network_level_or_blog_id ) ) {
154 $this->_network_options->unset_option( $option, $flush );
155 } else {
156 $site_options = $this->get_site_options( $network_level_or_blog_id );
157 $site_options->unset_option( $option, $flush );
158 }
159 }
160
161 /**
162 * @author Leo Fajardo (@leorw)
163 * @since 2.0.0
164 *
165 * @param bool $flush
166 * @param bool $network_level
167 */
168 function load( $flush = false, $network_level = true ) {
169 if ( $this->_is_multisite && $network_level ) {
170 $this->_network_options->load( $flush );
171 } else {
172 $this->_options->load( $flush );
173 }
174 }
175
176 /**
177 * @author Leo Fajardo (@leorw)
178 * @since 2.0.0
179 *
180 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, store both network storage and the current context blog storage.
181 */
182 function store( $network_level_or_blog_id = null ) {
183 if ( ! $this->_is_multisite ||
184 false === $network_level_or_blog_id ||
185 0 == $network_level_or_blog_id ||
186 is_null( $network_level_or_blog_id )
187 ) {
188 $site_options = $this->get_site_options( $network_level_or_blog_id );
189 $site_options->store();
190 }
191
192 if ( $this->_is_multisite &&
193 ( is_null( $network_level_or_blog_id ) || true === $network_level_or_blog_id )
194 ) {
195 $this->_network_options->store();
196 }
197 }
198
199 /**
200 * @author Vova Feldman (@svovaf)
201 * @since 2.0.0
202 *
203 * @param int|null|bool $network_level_or_blog_id
204 * @param bool $flush
205 */
206 function clear( $network_level_or_blog_id = null, $flush = false ) {
207 if ( ! $this->_is_multisite ||
208 false === $network_level_or_blog_id ||
209 is_null( $network_level_or_blog_id ) ||
210 is_numeric( $network_level_or_blog_id )
211 ) {
212 $site_options = $this->get_site_options( $network_level_or_blog_id );
213 $site_options->clear( $flush );
214 }
215
216 if ( $this->_is_multisite &&
217 ( true === $network_level_or_blog_id || is_null( $network_level_or_blog_id ) )
218 ) {
219 $this->_network_options->clear( $flush );
220 }
221 }
222
223 /**
224 * Migration script to the new storage data structure that is network compatible.
225 *
226 * IMPORTANT:
227 * This method should be executed only after it is determined if this is a network
228 * level compatible product activation.
229 *
230 * @author Vova Feldman (@svovaf)
231 * @since 2.0.0
232 *
233 * @param int $blog_id
234 */
235 function migrate_to_network( $blog_id = 0 ) {
236 if ( ! $this->_is_multisite ) {
237 return;
238 }
239
240 $updated = false;
241
242 $site_options = $this->get_site_options( $blog_id );
243
244 $keys = $site_options->get_options_keys();
245
246 foreach ( $keys as $option ) {
247 if ( $this->is_site_option( $option ) ||
248 // Don't move admin notices to the network storage.
249 in_array($option, array(
250 // Don't move admin notices to the network storage.
251 'admin_notices',
252 // Don't migrate the module specific data, it will be migrated by the FS_Storage.
253 'plugin_data',
254 'theme_data',
255 ))
256 ) {
257 continue;
258 }
259
260 $option_updated = false;
261
262 // Migrate option to the network storage.
263 $site_option = $site_options->get_option( $option );
264
265 if ( ! $this->_network_options->has_option( $option ) ) {
266 // Option not set on the network level, so just set it.
267 $this->_network_options->set_option( $option, $site_option, false );
268
269 $option_updated = true;
270 } else {
271 // Option already set on the network level, so we need to merge it inelegantly.
272 $network_option = $this->_network_options->get_option( $option );
273
274 if ( is_array( $network_option ) && is_array( $site_option ) ) {
275 // Option is an array.
276 foreach ( $site_option as $key => $value ) {
277 if ( ! isset( $network_option[ $key ] ) ) {
278 $network_option[ $key ] = $value;
279
280 $option_updated = true;
281 } else if ( is_array( $network_option[ $key ] ) && is_array( $value ) ) {
282 if ( empty( $network_option[ $key ] ) ) {
283 $network_option[ $key ] = $value;
284
285 $option_updated = true;
286 } else if ( empty( $value ) ) {
287 // Do nothing.
288 } else {
289 reset($value);
290 $first_key = key($value);
291 if ( $value[$first_key] instanceof FS_Entity ) {
292 // Merge entities by IDs.
293 $network_entities_ids = array();
294 foreach ( $network_option[ $key ] as $entity ) {
295 $network_entities_ids[ $entity->id ] = true;
296 }
297
298 foreach ( $value as $entity ) {
299 if ( ! isset( $network_entities_ids[ $entity->id ] ) ) {
300 $network_option[ $key ][] = $entity;
301
302 $option_updated = true;
303 }
304 }
305 }
306 }
307 }
308 }
309 }
310
311 if ( $option_updated ) {
312 $this->_network_options->set_option( $option, $network_option, false );
313 }
314 }
315
316 /**
317 * Remove the option from site level storage.
318 *
319 * IMPORTANT:
320 * The line below is intentionally commented since we want to preserve the option
321 * on the site storage level for "downgrade compatibility". Basically, if the user
322 * will downgrade to an older version of the plugin with the prev storage structure,
323 * it will continue working.
324 *
325 * @todo After a few releases we can remove this.
326 */
327 // $site_options->unset_option($option, false);
328
329 if ( $option_updated ) {
330 $updated = true;
331 }
332 }
333
334 if ( ! $updated ) {
335 return;
336 }
337
338 // Update network level storage.
339 $this->_network_options->store();
340 // $site_options->store();
341 }
342
343
344 #--------------------------------------------------------------------------------
345 #region Helper Methods
346 #--------------------------------------------------------------------------------
347
348 /**
349 * We don't want to load the map right away since it's not even needed in a non-MS environment.
350 *
351 * @author Vova Feldman (@svovaf)
352 * @since 2.0.0
353 */
354 private static function load_site_options_map() {
355 self::$_SITE_OPTIONS_MAP = array(
356 'sites' => true,
357 'theme_sites' => true,
358 'unique_id' => true,
359 'active_plugins' => true,
360 );
361 }
362
363 /**
364 * @author Vova Feldman (@svovaf)
365 * @since 2.0.0
366 *
367 * @param string $option
368 *
369 * @return bool
370 */
371 private function is_site_option( $option ) {
372 if ( WP_FS__ACCOUNTS_OPTION_NAME != $this->_id ) {
373 return false;
374 }
375
376 if ( ! isset( self::$_SITE_OPTIONS_MAP ) ) {
377 self::load_site_options_map();
378 }
379
380 return isset( self::$_SITE_OPTIONS_MAP[ $option ] );
381 }
382
383 /**
384 * @author Vova Feldman (@svovaf)
385 * @since 2.0.0
386 *
387 * @param int $blog_id
388 *
389 * @return FS_Option_Manager
390 */
391 private function get_site_options( $blog_id = 0 ) {
392 if ( 0 == $blog_id || $blog_id == $this->_blog_id ) {
393 return $this->_options;
394 }
395
396 return FS_Option_Manager::get_manager( $this->_id, true, $blog_id );
397 }
398
399 /**
400 * Check if an option should be stored on the MS network storage.
401 *
402 * @author Vova Feldman (@svovaf)
403 * @since 2.0.0
404 *
405 * @param string $option
406 * @param null|bool|int $network_level_or_blog_id When an integer, use the given blog storage. When `true` use the multisite storage (if there's a network). When `false`, use the current context blog storage. When `null`, the decision which storage to use (MS vs. Current S) will be handled internally and determined based on the $option (based on self::$_SITE_LEVEL_PARAMS).
407 *
408 * @return bool
409 */
410 private function should_use_network_storage( $option, $network_level_or_blog_id = null ) {
411 if ( ! $this->_is_multisite ) {
412 // Not a multisite environment.
413 return false;
414 }
415
416 if ( is_numeric( $network_level_or_blog_id ) ) {
417 // Explicitly asked to use a specified blog storage.
418 return false;
419 }
420
421 if ( is_bool( $network_level_or_blog_id ) ) {
422 // Explicitly specified whether should use the network or blog level storage.
423 return $network_level_or_blog_id;
424 }
425
426 // Determine which storage to use based on the option.
427 return ! $this->is_site_option( $option );
428 }
429
430 #endregion
431 }