GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

FS_Option_Manager::is_empty()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 25 and the first side effect is on line 10.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 {
0 ignored issues
show
Coding Style introduced by
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
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;
0 ignored issues
show
Documentation Bug introduced by
It seems like $network_level_or_blog_id can also be of type double or string. However, the property $_blog_id is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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));
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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() {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
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
    }