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.
Completed
Push — develop ( 1b89ee...33d1e4 )
by Brad
06:45 queued 03:33
created

FS_Plugin_Updater::set_update_data()   C

Complexity

Conditions 12
Paths 82

Size

Total Lines 59
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 34
nc 82
nop 1
dl 0
loc 59
rs 6.4485
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 15 and the first side effect is on line 12.

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.4
7
     *
8
     * @link        https://github.com/easydigitaldownloads/EDD-License-handler/blob/master/EDD_SL_Plugin_Updater.php
9
     */
10
11
    if ( ! defined( 'ABSPATH' ) ) {
12
        exit;
13
    }
14
15
    class FS_Plugin_Updater {
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...
16
17
        /**
18
         * @var Freemius
19
         * @since 1.0.4
20
         */
21
        private $_fs;
22
        /**
23
         * @var FS_Logger
24
         * @since 1.0.4
25
         */
26
        private $_logger;
27
        /**
28
         * @var object
29
         * @since 1.1.8.1
30
         */
31
        private $_update_details;
32
33
        #--------------------------------------------------------------------------------
34
        #region Singleton
35
        #--------------------------------------------------------------------------------
36
37
        /**
38
         * @var FS_Plugin_Updater[]
39
         * @since 2.0.0
40
         */
41
        private static $_INSTANCES = array();
42
43
        /**
44
         * @param Freemius $freemius
45
         *
46
         * @return FS_Plugin_Updater
47
         */
48
        static function instance( Freemius $freemius ) {
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...
49
            $key = $freemius->get_id();
50
51
            if ( ! isset( self::$_INSTANCES[ $key ] ) ) {
52
                self::$_INSTANCES[ $key ] = new self( $freemius );
53
            }
54
55
            return self::$_INSTANCES[ $key ];
56
        }
57
58
        #endregion
59
60
        private function __construct( Freemius $freemius ) {
61
            $this->_fs = $freemius;
62
63
            $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $freemius->get_slug() . '_updater', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
64
65
            $this->filters();
66
        }
67
68
        /**
69
         * Initiate required filters.
70
         *
71
         * @author Vova Feldman (@svovaf)
72
         * @since  1.0.4
73
         */
74
        private function filters() {
75
            // Override request for plugin information
76
            add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
77
78
            $this->add_transient_filters();
79
80
            if ( ! $this->_fs->has_active_valid_license() ) {
81
                /**
82
                 * If user has the premium plugin's code but do NOT have an active license,
83
                 * encourage him to upgrade by showing that there's a new release, but instead
84
                 * of showing an update link, show upgrade link to the pricing page.
85
                 *
86
                 * @since 1.1.6
87
                 *
88
                 */
89
                // WP 2.9+
90
                add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array(
91
                    &$this,
92
                    'catch_plugin_update_row'
93
                ), 9 );
94
                add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array(
95
                    &$this,
96
                    'edit_and_echo_plugin_update_row'
97
                ), 11, 2 );
98
            }
99
100
            if ( ! WP_FS__IS_PRODUCTION_MODE ) {
101
                add_filter( 'http_request_host_is_external', array(
102
                    $this,
103
                    'http_request_host_is_external_filter'
104
                ), 10, 3 );
105
            }
106
107
            if ( $this->_fs->is_premium() && $this->is_correct_folder_name() ) {
108
                add_filter( 'upgrader_post_install', array( &$this, '_maybe_update_folder_name' ), 10, 3 );
109
            }
110
        }
111
112
        /**
113
         * @author Vova Feldman (@svovaf)
114
         * @since  2.0.0
115
         */
116
        private function add_transient_filters() {
117
            add_filter( 'pre_set_site_transient_update_plugins', array(
118
                &$this,
119
                'pre_set_site_transient_update_plugins_filter'
120
            ) );
121
122
            add_filter( 'pre_set_site_transient_update_themes', array(
123
                &$this,
124
                'pre_set_site_transient_update_plugins_filter'
125
            ) );
126
        }
127
128
        /**
129
         * @author Vova Feldman (@svovaf)
130
         * @since  2.0.0
131
         */
132
        private function remove_transient_filters() {
133
            remove_filter( 'pre_set_site_transient_update_plugins', array(
134
                &$this,
135
                'pre_set_site_transient_update_plugins_filter'
136
            ) );
137
138
            remove_filter( 'pre_set_site_transient_update_themes', array(
139
                &$this,
140
                'pre_set_site_transient_update_plugins_filter'
141
            ) );
142
        }
143
144
        /**
145
         * Capture plugin update row by turning output buffering.
146
         *
147
         * @author Vova Feldman (@svovaf)
148
         * @since  1.1.6
149
         */
150
        function catch_plugin_update_row() {
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...
151
            ob_start();
152
        }
153
154
        /**
155
         * Overrides default update message format with "renew your license" message.
156
         *
157
         * @author Vova Feldman (@svovaf)
158
         * @since  1.1.6
159
         *
160
         * @param string $file
161
         * @param array  $plugin_data
162
         */
163
        function edit_and_echo_plugin_update_row( $file, $plugin_data ) {
0 ignored issues
show
Unused Code introduced by
The parameter $plugin_data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
164
            $plugin_update_row = ob_get_clean();
165
166
            $current = get_site_transient( 'update_plugins' );
167
            if ( ! isset( $current->response[ $file ] ) ) {
168
                echo $plugin_update_row;
169
170
                return;
171
            }
172
173
            $r = $current->response[ $file ];
174
175
            $plugin_update_row = preg_replace(
176
                '/(\<div.+>)(.+)(\<a.+\<a.+)\<\/div\>/is',
177
                '$1 $2 ' . sprintf(
178
                    $this->_fs->get_text_inline( '%sRenew your license now%s to access version %s features and support.', 'renew-license-now' ),
179
                    '<a href="' . $this->_fs->pricing_url() . '">', '</a>',
180
                    $r->new_version ) .
181
                '$4',
182
                $plugin_update_row
183
            );
184
185
            echo $plugin_update_row;
186
        }
187
188
        /**
189
         * Since WP version 3.6, a new security feature was added that denies access to repository with a local ip.
190
         * During development mode we want to be able updating plugin versions via our localhost repository. This
191
         * filter white-list all domains including "api.freemius".
192
         *
193
         * @link   http://www.emanueletessore.com/wordpress-download-failed-valid-url-provided/
194
         *
195
         * @author Vova Feldman (@svovaf)
196
         * @since  1.0.4
197
         *
198
         * @param bool   $allow
199
         * @param string $host
200
         * @param string $url
201
         *
202
         * @return bool
203
         */
204
        function http_request_host_is_external_filter( $allow, $host, $url ) {
0 ignored issues
show
Unused Code introduced by
The parameter $url is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
205
            return ( false !== strpos( $host, 'freemius' ) ) ? true : $allow;
206
        }
207
208
        /**
209
         * Check for Updates at the defined API endpoint and modify the update array.
210
         *
211
         * This function dives into the update api just when WordPress creates its update array,
212
         * then adds a custom API call and injects the custom plugin data retrieved from the API.
213
         * It is reassembled from parts of the native WordPress plugin update code.
214
         * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
215
         *
216
         * @author Vova Feldman (@svovaf)
217
         * @since  1.0.4
218
         *
219
         * @uses   FS_Api
220
         *
221
         * @param object $transient_data Update array build by WordPress.
222
         *
223
         * @return object Modified update array with custom plugin data.
224
         */
225
        function pre_set_site_transient_update_plugins_filter( $transient_data ) {
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...
226
            $this->_logger->entrance();
227
228
            /**
229
             * "plugins" or "themes".
230
             *
231
             * @author Leo Fajardo (@leorw)
232
             * @since  1.2.2
233
             */
234
            $module_type = $this->_fs->get_module_type() . 's';
235
236
            /**
237
             * Ensure that we don't mix plugins update info with themes update info.
238
             *
239
             * @author Leo Fajardo (@leorw)
240
             * @since  1.2.2
241
             */
242
            if ( "pre_set_site_transient_update_{$module_type}" !== current_filter() ) {
243
                return $transient_data;
244
            }
245
246
            if ( empty( $transient_data ) ||
247
                 defined( 'WP_FS__UNINSTALL_MODE' )
248
            ) {
249
                return $transient_data;
250
            }
251
252
            if ( ! isset( $this->_update_details ) ) {
253
                // Get plugin's newest update.
254
                $new_version = $this->_fs->get_update(
255
                    false,
256
                    fs_request_get_bool( 'force-check' ),
257
                    WP_FS__TIME_24_HOURS_IN_SEC / 24
258
                );
259
260
                $this->_update_details = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type object of property $_update_details.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
261
262
                if ( is_object( $new_version ) ) {
263
                    $this->_logger->log( 'Found newer plugin version ' . $new_version->version );
264
265
                    /**
266
                     * Cache plugin details locally since set_site_transient( 'update_plugins' )
267
                     * called multiple times and the non wp.org plugins are filtered after the
268
                     * call to .org.
269
                     *
270
                     * @since 1.1.8.1
271
                     */
272
                    $this->_update_details = $this->get_update_details( $new_version );
273
                }
274
            }
275
276
            if ( is_object( $this->_update_details ) ) {
277
                // Add plugin to transient data.
278
                $transient_data->response[ $this->_fs->get_plugin_basename() ] = $this->_fs->is_plugin() ?
279
                    $this->_update_details :
280
                    (array) $this->_update_details;
281
            }
282
283
            return $transient_data;
284
        }
285
286
        /**
287
         * Get module's required data for the updates mechanism.
288
         *
289
         * @author Vova Feldman (@svovaf)
290
         * @since  2.0.0
291
         *
292
         * @param \FS_Plugin_Tag $new_version
293
         *
294
         * @return object
295
         */
296
        function get_update_details( FS_Plugin_Tag $new_version ) {
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...
297
            $update              = new stdClass();
298
            $update->slug        = $this->_fs->get_slug();
299
            $update->new_version = $new_version->version;
300
            $update->url         = WP_FS__ADDRESS;
301
            $update->package     = $new_version->url;
302
            $update->tested      = $new_version->tested_up_to_version;
303
            $update->requires    = $new_version->requires_platform_version;
304
305
            $icon = $this->_fs->get_local_icon_url();
306
307
            if ( ! empty( $icon ) ) {
308
                $update->icons = array(
309
//                    '1x'      => $icon,
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% 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...
310
//                    '2x'      => $icon,
311
                    'default' => $icon,
312
                );
313
            }
314
315
            $update->{$this->_fs->get_module_type()} = $this->_fs->get_plugin_basename();
316
317
            return $update;
318
        }
319
320
        /**
321
         * Update the updates transient with the module's update information.
322
         *
323
         * This method is required for multisite environment.
324
         * If a module is site activated (not network) and not on the main site,
325
         * the module will NOT be executed on the network level, therefore, the
326
         * custom updates logic will not be executed as well, so unless we force
327
         * the injection of the update into the updates transient, premium updates
328
         * will not work.
329
         *
330
         * @author Vova Feldman (@svovaf)
331
         * @since  2.0.0
332
         *
333
         * @param \FS_Plugin_Tag $new_version
334
         */
335
        function set_update_data( FS_Plugin_Tag $new_version ) {
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...
336
            $this->_logger->entrance();
337
338
            $transient_key = "update_{$this->_fs->get_module_type()}s";
339
340
            $transient_data = get_site_transient( $transient_key );
341
342
            $transient_data = is_object( $transient_data ) ?
343
                $transient_data :
344
                new stdClass();
345
346
            // Alias.
347
            $basename  = $this->_fs->get_plugin_basename();
348
            $is_plugin = $this->_fs->is_plugin();
349
350
            if ( ! isset( $transient_data->response ) ||
351
                 ! is_array( $transient_data->response )
352
            ) {
353
                $transient_data->response = array();
354
            } else if ( ! empty( $transient_data->response[ $basename ] ) ) {
355
                $version = $is_plugin ?
356
                    ( ! empty( $transient_data->response[ $basename ]->new_version ) ?
357
                        $transient_data->response[ $basename ]->new_version :
358
                        null
359
                    ) : ( ! empty( $transient_data->response[ $basename ]['new_version'] ) ?
360
                        $transient_data->response[ $basename ]['new_version'] :
361
                        null
362
                    );
363
364
                if ( $version == $new_version->version ) {
365
                    // The update data is already set.
366
                    return;
367
                }
368
            }
369
370
            // Remove the added filters.
371
            $this->remove_transient_filters();
372
373
            $this->_update_details = $this->get_update_details( $new_version );
374
375
            // Set update data in transient.
376
            $transient_data->response[ $basename ] = $is_plugin ?
377
                $this->_update_details :
378
                (array) $this->_update_details;
379
380
            if ( ! isset( $transient_data->checked ) ||
381
                 ! is_array( $transient_data->checked )
382
            ) {
383
                $transient_data->checked = array();
384
            }
385
386
            // Flag the module as if it was already checked.
387
            $transient_data->checked[ $basename ] = $this->_fs->get_plugin_version();
388
            $transient_data->last_checked         = time();
389
390
            set_site_transient( $transient_key, $transient_data );
391
392
            $this->add_transient_filters();
393
        }
394
395
        /**
396
         * Try to fetch plugin's info from .org repository.
397
         *
398
         * @author Vova Feldman (@svovaf)
399
         * @since  1.0.5
400
         *
401
         * @param string $action
402
         * @param object $args
403
         *
404
         * @return bool|mixed
405
         */
406
        static function _fetch_plugin_info_from_repository( $action, $args ) {
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...
407
            $url = $http_url = 'http://api.wordpress.org/plugins/info/1.0/';
0 ignored issues
show
Unused Code introduced by
$http_url is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
408
            if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
0 ignored issues
show
Unused Code introduced by
$ssl is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
409
                $url = set_url_scheme( $url, 'https' );
410
            }
411
412
            $args = array(
413
                'timeout' => 15,
414
                'body'    => array(
415
                    'action'  => $action,
416
                    'request' => serialize( $args )
417
                )
418
            );
419
420
            $request = wp_remote_post( $url, $args );
421
422
            if ( is_wp_error( $request ) ) {
423
                return false;
424
            }
425
426
            $res = maybe_unserialize( wp_remote_retrieve_body( $request ) );
427
428
            if ( ! is_object( $res ) && ! is_array( $res ) ) {
429
                return false;
430
            }
431
432
            return $res;
433
        }
434
435
        /**
436
         * Updates information on the "View version x.x details" page with custom data.
437
         *
438
         * @author Vova Feldman (@svovaf)
439
         * @since  1.0.4
440
         *
441
         * @uses   FS_Api
442
         *
443
         * @param object $data
444
         * @param string $action
445
         * @param mixed  $args
446
         *
447
         * @return object
448
         */
449
        function plugins_api_filter( $data, $action = '', $args = 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...
450
            $this->_logger->entrance();
451
452
            if ( ( 'plugin_information' !== $action ) ||
453
                 ! isset( $args->slug )
454
            ) {
455
                return $data;
456
            }
457
458
            $addon    = false;
459
            $is_addon = false;
460
461
            if ( $this->_fs->get_slug() !== $args->slug ) {
462
                $addon = $this->_fs->get_addon_by_slug( $args->slug );
463
464
                if ( ! is_object( $addon ) ) {
465
                    return $data;
466
                }
467
468
                $is_addon = true;
469
            }
470
471
            $plugin_in_repo = false;
472
            if ( ! $is_addon ) {
473
                // Try to fetch info from .org repository.
474
                $data = self::_fetch_plugin_info_from_repository( $action, $args );
475
476
                $plugin_in_repo = ( false !== $data );
477
            }
478
479
            if ( ! $plugin_in_repo ) {
480
                $data = $args;
481
482
                // Fetch as much as possible info from local files.
483
                $plugin_local_data = $this->_fs->get_plugin_data();
484
                $data->name        = $plugin_local_data['Name'];
485
                $data->author      = $plugin_local_data['Author'];
486
                $data->sections    = array(
487
                    'description' => 'Upgrade ' . $plugin_local_data['Name'] . ' to latest.',
488
                );
489
490
                // @todo Store extra plugin info on Freemius or parse readme.txt markup.
491
                /*$info = $this->_fs->get_api_site_scope()->call('/information.json');
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
492
493
if ( !isset($info->error) ) {
494
    $data = $info;
495
}*/
496
            }
497
498
            // Get plugin's newest update.
499
            $new_version = $this->get_latest_download_details( $is_addon ? $addon->id : false );
500
501
            if ( ! is_object( $new_version ) || empty( $new_version->version ) ) {
502
                $data->version = $this->_fs->get_plugin_version();
503
            } else {
504
                if ( $is_addon ) {
505
                    $data->name    = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
506
                    $data->slug    = $addon->slug;
507
                    $data->url     = WP_FS__ADDRESS;
508
                    $data->package = $new_version->url;
509
                }
510
511
                if ( ! $plugin_in_repo ) {
512
                    $data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created;
513
                    $data->requires     = $new_version->requires_platform_version;
514
                    $data->tested       = $new_version->tested_up_to_version;
515
                }
516
517
                $data->version       = $new_version->version;
518
                $data->download_link = $new_version->url;
519
            }
520
521
            return $data;
522
        }
523
524
        /**
525
         * @author Vova Feldman (@svovaf)
526
         * @since  1.2.1.7
527
         *
528
         * @param number|bool $addon_id
529
         *
530
         * @return object
531
         */
532
        private function get_latest_download_details( $addon_id = false ) {
533
            return $this->_fs->_fetch_latest_version( $addon_id );
534
        }
535
536
        /**
537
         * Checks if a given basename has a matching folder name
538
         * with the current context plugin.
539
         *
540
         * @author Vova Feldman (@svovaf)
541
         * @since  1.2.1.6
542
         *
543
         * @param string $basename Current plugin's basename.
544
         *
545
         * @return bool
546
         */
547
        private function is_correct_folder_name( $basename = '' ) {
548
            if ( empty( $basename ) ) {
549
                $basename = $this->_fs->get_plugin_basename();
550
            }
551
552
            return ( $this->_fs->get_target_folder_name() != trim( dirname( $basename ), '/\\' ) );
553
        }
554
555
        /**
556
         * This is a special after upgrade handler for migrating modules
557
         * that didn't use the '-premium' suffix folder structure before
558
         * the migration.
559
         *
560
         * @author Vova Feldman (@svovaf)
561
         * @since  1.2.1.6
562
         *
563
         * @param bool  $response   Install response.
564
         * @param array $hook_extra Extra arguments passed to hooked filters.
565
         * @param array $result     Installation result data.
566
         *
567
         * @return bool
568
         */
569
        function _maybe_update_folder_name( $response, $hook_extra, $result ) {
0 ignored issues
show
Unused Code introduced by
The parameter $result is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
570
            $basename = $this->_fs->get_plugin_basename();
571
572
            if ( true !== $response ||
573
                 empty( $hook_extra ) ||
574
                 empty( $hook_extra['plugin'] ) ||
575
                 $basename !== $hook_extra['plugin']
576
            ) {
577
                return $response;
578
            }
579
580
            $active_plugins_basenames = get_option( 'active_plugins' );
581
582
            for ( $i = 0, $len = count( $active_plugins_basenames ); $i < $len; $i ++ ) {
583
                if ( $basename === $active_plugins_basenames[ $i ] ) {
584
                    // Get filename including extension.
585
                    $filename = basename( $basename );
586
587
                    $new_basename = plugin_basename(
588
                        trailingslashit( $this->_fs->get_slug() . ( $this->_fs->is_premium() ? '-premium' : '' ) ) .
589
                        $filename
590
                    );
591
592
                    // Verify that the expected correct path exists.
593
                    if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $new_basename ) ) ) {
594
                        // Override active plugin name.
595
                        $active_plugins_basenames[ $i ] = $new_basename;
596
                        update_option( 'active_plugins', $active_plugins_basenames );
597
                    }
598
599
                    break;
600
                }
601
            }
602
603
            return $response;
604
        }
605
606
        #----------------------------------------------------------------------------------
607
        #region Auto Activation
608
        #----------------------------------------------------------------------------------
609
610
        /**
611
         * Installs and active a plugin when explicitly requested that from a 3rd party service.
612
         *
613
         * This logic was inspired by the TGMPA GPL licensed library by Thomas Griffin.
614
         *
615
         * @link   http://tgmpluginactivation.com/
616
         *
617
         * @author Vova Feldman
618
         * @since  1.2.1.7
619
         *
620
         * @link   https://make.wordpress.org/plugins/2017/03/16/clarification-of-guideline-8-executable-code-and-installs/
621
         *
622
         * @uses   WP_Filesystem
623
         * @uses   WP_Error
624
         * @uses   WP_Upgrader
625
         * @uses   Plugin_Upgrader
626
         * @uses   Plugin_Installer_Skin
627
         * @uses   Plugin_Upgrader_Skin
628
         *
629
         * @param number|bool $plugin_id
630
         *
631
         * @return array
632
         */
633
        function install_and_activate_plugin( $plugin_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...
634
            if ( ! empty( $plugin_id ) && ! FS_Plugin::is_valid_id( $plugin_id ) ) {
635
                // Invalid plugin ID.
636
                return array(
637
                    'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
638
                    'code'    => 'invalid_module_id',
639
                );
640
            }
641
642
            $is_addon = false;
643
            if ( FS_Plugin::is_valid_id( $plugin_id ) &&
644
                 $plugin_id != $this->_fs->get_id()
645
            ) {
646
                $addon = $this->_fs->get_addon( $plugin_id );
647
648
                if ( ! is_object( $addon ) ) {
649
                    // Invalid add-on ID.
650
                    return array(
651
                        'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
652
                        'code'    => 'invalid_module_id',
653
                    );
654
                }
655
656
                $slug  = $addon->slug;
657
                $title = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
658
659
                $is_addon = true;
660
            } else {
661
                $slug  = $this->_fs->get_slug();
662
                $title = $this->_fs->get_plugin_title() .
663
                         ( $this->_fs->is_addon() ? ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ) : '' );
664
            }
665
666
            if ( $this->is_premium_plugin_active( $plugin_id ) ) {
667
                // Premium version already activated.
668
                return array(
669
                    'message' => $is_addon ?
670
                        $this->_fs->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ) :
671
                        $this->_fs->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ),
672
                    'code'    => 'premium_installed',
673
                );
674
            }
675
676
            $latest_version = $this->get_latest_download_details( $plugin_id );
677
            $target_folder  = "{$slug}-premium";
678
679
            // Prep variables for Plugin_Installer_Skin class.
680
            $extra         = array();
681
            $extra['slug'] = $target_folder;
682
            $source        = $latest_version->url;
683
            $api           = null;
684
685
            $install_url = add_query_arg(
686
                array(
687
                    'action' => 'install-plugin',
688
                    'plugin' => urlencode( $slug ),
689
                ),
690
                'update.php'
691
            );
692
693
            if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
694
                // Include required resources for the installation.
695
                require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
696
            }
697
698
            $skin_args = array(
699
                'type'   => 'web',
700
                'title'  => sprintf( $this->_fs->get_text_inline( 'Installing plugin: %s', 'installing-plugin-x' ), $title ),
701
                'url'    => esc_url_raw( $install_url ),
702
                'nonce'  => 'install-plugin_' . $slug,
703
                'plugin' => '',
704
                'api'    => $api,
705
                'extra'  => $extra,
706
            );
707
708
//			$skin = new Automatic_Upgrader_Skin( $skin_args );
0 ignored issues
show
Unused Code Comprehensibility introduced by
42% 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...
709
//			$skin = new Plugin_Installer_Skin( $skin_args );
710
            $skin = new WP_Ajax_Upgrader_Skin( $skin_args );
711
712
            // Create a new instance of Plugin_Upgrader.
713
            $upgrader = new Plugin_Upgrader( $skin );
714
715
            // Perform the action and install the plugin from the $source urldecode().
716
            add_filter( 'upgrader_source_selection', array( &$this, '_maybe_adjust_source_dir' ), 1, 3 );
717
718
            $install_result = $upgrader->install( $source );
719
720
            remove_filter( 'upgrader_source_selection', array( &$this, '_maybe_adjust_source_dir' ), 1 );
721
722
            if ( is_wp_error( $install_result ) ) {
723
                return array(
724
                    'message' => $install_result->get_error_message(),
725
                    'code'    => $install_result->get_error_code(),
726
                );
727
            } elseif ( is_wp_error( $skin->result ) ) {
728
                return array(
729
                    'message' => $skin->result->get_error_message(),
730
                    'code'    => $skin->result->get_error_code(),
731
                );
732
            } elseif ( $skin->get_errors()->get_error_code() ) {
733
                return array(
734
                    'message' => $skin->get_error_messages(),
735
                    'code'    => 'unknown',
736
                );
737
            } elseif ( is_null( $install_result ) ) {
738
                global $wp_filesystem;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
739
740
                $error_code    = 'unable_to_connect_to_filesystem';
741
                $error_message = $this->_fs->get_text_inline( 'Unable to connect to the filesystem. Please confirm your credentials.' );
742
743
                // Pass through the error from WP_Filesystem if one was raised.
744
                if ( $wp_filesystem instanceof WP_Filesystem_Base &&
0 ignored issues
show
Bug introduced by
The class WP_Filesystem_Base does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
745
                     is_wp_error( $wp_filesystem->errors ) &&
746
                     $wp_filesystem->errors->get_error_code()
747
                ) {
748
                    $error_message = $wp_filesystem->errors->get_error_message();
749
                }
750
751
                return array(
752
                    'message' => $error_message,
753
                    'code'    => $error_code,
754
                );
755
            }
756
757
            // Grab the full path to the main plugin's file.
758
            $plugin_activate = $upgrader->plugin_info();
759
760
            // Try to activate the plugin.
761
            $activation_result = $this->try_activate_plugin( $plugin_activate );
762
763
            if ( is_wp_error( $activation_result ) ) {
764
                return array(
765
                    'message' => $activation_result->get_error_message(),
766
                    'code'    => $activation_result->get_error_code(),
767
                );
768
            }
769
770
            return $skin->get_upgrade_messages();
771
        }
772
773
        /**
774
         * Tries to activate a plugin. If fails, returns the error.
775
         *
776
         * @author Vova Feldman
777
         * @since  1.2.1.7
778
         *
779
         * @param string $file_path Path within wp-plugins/ to main plugin file.
780
         *                          This determines the styling of the output messages.
781
         *
782
         * @return bool|WP_Error
783
         */
784
        protected function try_activate_plugin( $file_path ) {
785
            $activate = activate_plugin( $file_path, '', $this->_fs->is_network_active() );
786
787
            return is_wp_error( $activate ) ?
788
                $activate :
789
                true;
790
        }
791
792
        /**
793
         * Check if a premium module version is already active.
794
         *
795
         * @author Vova Feldman
796
         * @since  1.2.1.7
797
         *
798
         * @param number|bool $plugin_id
799
         *
800
         * @return bool
801
         */
802
        private function is_premium_plugin_active( $plugin_id = false ) {
803
            if ( $plugin_id != $this->_fs->get_id() ) {
804
                return $this->_fs->is_addon_activated( $plugin_id, true );
805
            }
806
807
            return is_plugin_active( $this->_fs->premium_plugin_basename() );
808
        }
809
810
        /**
811
         * Adjust the plugin directory name if necessary.
812
         * Assumes plugin has a folder (not a single file plugin).
813
         *
814
         * The final destination directory of a plugin is based on the subdirectory name found in the
815
         * (un)zipped source. In some cases this subdirectory name is not the same as the expected
816
         * slug and the plugin will not be recognized as installed. This is fixed by adjusting
817
         * the temporary unzipped source subdirectory name to the expected plugin slug.
818
         *
819
         * @author Vova Feldman
820
         * @since  1.2.1.7
821
         *
822
         * @param string       $source        Path to upgrade/zip-file-name.tmp/subdirectory/.
823
         * @param string       $remote_source Path to upgrade/zip-file-name.tmp.
824
         * @param \WP_Upgrader $upgrader      Instance of the upgrader which installs the plugin.
825
         *
826
         * @return string|WP_Error
827
         */
828
        function _maybe_adjust_source_dir( $source, $remote_source, $upgrader ) {
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...
Coding Style introduced by
_maybe_adjust_source_dir uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

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

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
829
            if ( ! is_object( $GLOBALS['wp_filesystem'] ) ) {
830
                return $source;
831
            }
832
833
            // Figure out what the slug is supposed to be.
834
            $desired_slug = $upgrader->skin->options['extra']['slug'];
835
836
            $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
837
838
            if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
839
                $from_path = untrailingslashit( $source );
840
                $to_path   = trailingslashit( $remote_source ) . $desired_slug;
841
842
                if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) {
843
                    return trailingslashit( $to_path );
844
                } else {
845
                    return new WP_Error(
846
                        'rename_failed',
847
                        $this->_fs->get_text_inline( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'module-package-rename-failure' ),
848
                        array(
849
                            'found'    => $subdir_name,
850
                            'expected' => $desired_slug
851
                        ) );
852
                }
853
            }
854
855
            return $source;
856
        }
857
858
        #endregion
859
    }