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 — master ( 737b79...d4ff54 )
by Brad
02:47
created

FS_Plugin_Updater::change_theme_update_info_html()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 29

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 29
rs 9.456
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 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() ) {
108
                if ( $this->is_correct_folder_name() ) {
109
                    add_filter( 'upgrader_post_install', array( &$this, '_maybe_update_folder_name' ), 10, 3 );
110
                }
111
112
                if ( ! $this->_fs->has_active_valid_license() ) {
113
                    add_filter( 'wp_prepare_themes_for_js', array( &$this, 'change_theme_update_info_html' ), 10, 1 );
114
                }
115
            }
116
        }
117
118
        /**
119
         * @author Vova Feldman (@svovaf)
120
         * @since  2.0.0
121
         */
122
        private function add_transient_filters() {
123
            add_filter( 'pre_set_site_transient_update_plugins', array(
124
                &$this,
125
                'pre_set_site_transient_update_plugins_filter'
126
            ) );
127
128
            add_filter( 'pre_set_site_transient_update_themes', array(
129
                &$this,
130
                'pre_set_site_transient_update_plugins_filter'
131
            ) );
132
        }
133
134
        /**
135
         * @author Vova Feldman (@svovaf)
136
         * @since  2.0.0
137
         */
138
        private function remove_transient_filters() {
139
            remove_filter( 'pre_set_site_transient_update_plugins', array(
140
                &$this,
141
                'pre_set_site_transient_update_plugins_filter'
142
            ) );
143
144
            remove_filter( 'pre_set_site_transient_update_themes', array(
145
                &$this,
146
                'pre_set_site_transient_update_plugins_filter'
147
            ) );
148
        }
149
150
        /**
151
         * Capture plugin update row by turning output buffering.
152
         *
153
         * @author Vova Feldman (@svovaf)
154
         * @since  1.1.6
155
         */
156
        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...
157
            ob_start();
158
        }
159
160
        /**
161
         * Overrides default update message format with "renew your license" message.
162
         *
163
         * @author Vova Feldman (@svovaf)
164
         * @since  1.1.6
165
         *
166
         * @param string $file
167
         * @param array  $plugin_data
168
         */
169
        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...
170
            $plugin_update_row = ob_get_clean();
171
172
            $current = get_site_transient( 'update_plugins' );
173
            if ( ! isset( $current->response[ $file ] ) ) {
174
                echo $plugin_update_row;
175
176
                return;
177
            }
178
179
            $r = $current->response[ $file ];
180
181
            $plugin_update_row = preg_replace(
182
                '/(\<div.+>)(.+)(\<a.+\<a.+)\<\/div\>/is',
183
                '$1 $2 ' . sprintf(
184
                    $this->_fs->get_text_inline( '%sRenew your license now%s to access version %s security & feature updates, and support.', 'renew-license-now' ),
185
                    '<a href="' . $this->_fs->pricing_url() . '">', '</a>',
186
                    $r->new_version ) .
187
                '$4',
188
                $plugin_update_row
189
            );
190
191
            echo $plugin_update_row;
192
        }
193
194
        /**
195
         * @author Leo Fajardo (@leorw)
196
         * @since  2.0.2
197
         *
198
         * @param array $prepared_themes
199
         *
200
         * @return array
201
         */
202
        function change_theme_update_info_html( $prepared_themes ) {
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...
203
            $theme_basename = $this->_fs->get_plugin_basename();
204
205
            if ( ! isset( $prepared_themes[ $theme_basename ] ) ) {
206
                return $prepared_themes;
207
            }
208
209
            $themes_update = get_site_transient( 'update_themes' );
210
            if ( ! isset( $themes_update->response[ $theme_basename ] ) ||
211
                empty( $themes_update->response[ $theme_basename ]['package'] )
212
            ) {
213
                return $prepared_themes;
214
            }
215
216
            $prepared_themes[ $theme_basename ]['update'] = preg_replace(
217
                '/(\<p.+>)(.+)(\<a.+\<a.+)\.(.+\<\/p\>)/is',
218
                '$1 $2 ' . sprintf(
219
                    $this->_fs->get_text_inline( '%sRenew your license now%s to access version %s security & feature updates, and support.', 'renew-license-now' ),
220
                    '<a href="' . $this->_fs->pricing_url() . '">', '</a>',
221
                    $themes_update->response[ $theme_basename ]['new_version'] ) .
222
                '$4',
223
                $prepared_themes[ $theme_basename ]['update']
224
            );
225
226
            // Set to false to prevent the "Update now" link for the context theme from being shown on the "Themes" page.
227
            $prepared_themes[ $theme_basename ]['hasPackage'] = false;
228
229
            return $prepared_themes;
230
        }
231
232
        /**
233
         * Since WP version 3.6, a new security feature was added that denies access to repository with a local ip.
234
         * During development mode we want to be able updating plugin versions via our localhost repository. This
235
         * filter white-list all domains including "api.freemius".
236
         *
237
         * @link   http://www.emanueletessore.com/wordpress-download-failed-valid-url-provided/
238
         *
239
         * @author Vova Feldman (@svovaf)
240
         * @since  1.0.4
241
         *
242
         * @param bool   $allow
243
         * @param string $host
244
         * @param string $url
245
         *
246
         * @return bool
247
         */
248
        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...
249
            return ( false !== strpos( $host, 'freemius' ) ) ? true : $allow;
250
        }
251
252
        /**
253
         * Check for Updates at the defined API endpoint and modify the update array.
254
         *
255
         * This function dives into the update api just when WordPress creates its update array,
256
         * then adds a custom API call and injects the custom plugin data retrieved from the API.
257
         * It is reassembled from parts of the native WordPress plugin update code.
258
         * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
259
         *
260
         * @author Vova Feldman (@svovaf)
261
         * @since  1.0.4
262
         *
263
         * @uses   FS_Api
264
         *
265
         * @param object $transient_data Update array build by WordPress.
266
         *
267
         * @return object Modified update array with custom plugin data.
268
         */
269
        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...
270
            $this->_logger->entrance();
271
272
            /**
273
             * "plugins" or "themes".
274
             *
275
             * @author Leo Fajardo (@leorw)
276
             * @since  1.2.2
277
             */
278
            $module_type = $this->_fs->get_module_type() . 's';
279
280
            /**
281
             * Ensure that we don't mix plugins update info with themes update info.
282
             *
283
             * @author Leo Fajardo (@leorw)
284
             * @since  1.2.2
285
             */
286
            if ( "pre_set_site_transient_update_{$module_type}" !== current_filter() ) {
287
                return $transient_data;
288
            }
289
290
            if ( empty( $transient_data ) ||
291
                 defined( 'WP_FS__UNINSTALL_MODE' )
292
            ) {
293
                return $transient_data;
294
            }
295
296
            if ( ! isset( $this->_update_details ) ) {
297
                // Get plugin's newest update.
298
                $new_version = $this->_fs->get_update(
299
                    false,
300
                    fs_request_get_bool( 'force-check' ),
301
                    WP_FS__TIME_24_HOURS_IN_SEC / 24
302
                );
303
304
                $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...
305
306
                if ( is_object( $new_version ) ) {
307
                    $this->_logger->log( 'Found newer plugin version ' . $new_version->version );
308
309
                    /**
310
                     * Cache plugin details locally since set_site_transient( 'update_plugins' )
311
                     * called multiple times and the non wp.org plugins are filtered after the
312
                     * call to .org.
313
                     *
314
                     * @since 1.1.8.1
315
                     */
316
                    $this->_update_details = $this->get_update_details( $new_version );
317
                }
318
            }
319
320
            if ( is_object( $this->_update_details ) ) {
321
                // Add plugin to transient data.
322
                $transient_data->response[ $this->_fs->get_plugin_basename() ] = $this->_fs->is_plugin() ?
323
                    $this->_update_details :
324
                    (array) $this->_update_details;
325
            }
326
327
            return $transient_data;
328
        }
329
330
        /**
331
         * Get module's required data for the updates mechanism.
332
         *
333
         * @author Vova Feldman (@svovaf)
334
         * @since  2.0.0
335
         *
336
         * @param \FS_Plugin_Tag $new_version
337
         *
338
         * @return object
339
         */
340
        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...
341
            $update              = new stdClass();
342
            $update->slug        = $this->_fs->get_slug();
343
            $update->new_version = $new_version->version;
344
            $update->url         = WP_FS__ADDRESS;
345
            $update->package     = $new_version->url;
346
            $update->tested      = $new_version->tested_up_to_version;
347
            $update->requires    = $new_version->requires_platform_version;
348
349
            $icon = $this->_fs->get_local_icon_url();
350
351
            if ( ! empty( $icon ) ) {
352
                $update->icons = array(
353
//                    '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...
354
//                    '2x'      => $icon,
355
                    'default' => $icon,
356
                );
357
            }
358
359
            $update->{$this->_fs->get_module_type()} = $this->_fs->get_plugin_basename();
360
361
            return $update;
362
        }
363
364
        /**
365
         * Update the updates transient with the module's update information.
366
         *
367
         * This method is required for multisite environment.
368
         * If a module is site activated (not network) and not on the main site,
369
         * the module will NOT be executed on the network level, therefore, the
370
         * custom updates logic will not be executed as well, so unless we force
371
         * the injection of the update into the updates transient, premium updates
372
         * will not work.
373
         *
374
         * @author Vova Feldman (@svovaf)
375
         * @since  2.0.0
376
         *
377
         * @param \FS_Plugin_Tag $new_version
378
         */
379
        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...
380
            $this->_logger->entrance();
381
382
            $transient_key = "update_{$this->_fs->get_module_type()}s";
383
384
            $transient_data = get_site_transient( $transient_key );
385
386
            $transient_data = is_object( $transient_data ) ?
387
                $transient_data :
388
                new stdClass();
389
390
            // Alias.
391
            $basename  = $this->_fs->get_plugin_basename();
392
            $is_plugin = $this->_fs->is_plugin();
393
394
            if ( ! isset( $transient_data->response ) ||
395
                 ! is_array( $transient_data->response )
396
            ) {
397
                $transient_data->response = array();
398
            } else if ( ! empty( $transient_data->response[ $basename ] ) ) {
399
                $version = $is_plugin ?
400
                    ( ! empty( $transient_data->response[ $basename ]->new_version ) ?
401
                        $transient_data->response[ $basename ]->new_version :
402
                        null
403
                    ) : ( ! empty( $transient_data->response[ $basename ]['new_version'] ) ?
404
                        $transient_data->response[ $basename ]['new_version'] :
405
                        null
406
                    );
407
408
                if ( $version == $new_version->version ) {
409
                    // The update data is already set.
410
                    return;
411
                }
412
            }
413
414
            // Remove the added filters.
415
            $this->remove_transient_filters();
416
417
            $this->_update_details = $this->get_update_details( $new_version );
418
419
            // Set update data in transient.
420
            $transient_data->response[ $basename ] = $is_plugin ?
421
                $this->_update_details :
422
                (array) $this->_update_details;
423
424
            if ( ! isset( $transient_data->checked ) ||
425
                 ! is_array( $transient_data->checked )
426
            ) {
427
                $transient_data->checked = array();
428
            }
429
430
            // Flag the module as if it was already checked.
431
            $transient_data->checked[ $basename ] = $this->_fs->get_plugin_version();
432
            $transient_data->last_checked         = time();
433
434
            set_site_transient( $transient_key, $transient_data );
435
436
            $this->add_transient_filters();
437
        }
438
439
        /**
440
         * @author Leo Fajardo (@leorw)
441
         * @since 2.0.2
442
         */
443
        function delete_update_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...
444
            $this->_logger->entrance();
445
446
            $transient_key = "update_{$this->_fs->get_module_type()}s";
447
448
            $transient_data = get_site_transient( $transient_key );
449
450
            // Alias
451
            $basename = $this->_fs->get_plugin_basename();
452
453
            if ( ! is_object( $transient_data ) ||
454
                ! isset( $transient_data->response ) ||
455
                 ! is_array( $transient_data->response ) ||
456
                empty( $transient_data->response[ $basename ] )
457
            ) {
458
                return;
459
            }
460
461
            unset( $transient_data->response[ $basename ] );
462
463
            // Remove the added filters.
464
            $this->remove_transient_filters();
465
466
            set_site_transient( $transient_key, $transient_data );
467
468
            $this->add_transient_filters();
469
        }
470
471
        /**
472
         * Try to fetch plugin's info from .org repository.
473
         *
474
         * @author Vova Feldman (@svovaf)
475
         * @since  1.0.5
476
         *
477
         * @param string $action
478
         * @param object $args
479
         *
480
         * @return bool|mixed
481
         */
482
        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...
483
            $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...
484
            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...
485
                $url = set_url_scheme( $url, 'https' );
486
            }
487
488
            $args = array(
489
                'timeout' => 15,
490
                'body'    => array(
491
                    'action'  => $action,
492
                    'request' => serialize( $args )
493
                )
494
            );
495
496
            $request = wp_remote_post( $url, $args );
497
498
            if ( is_wp_error( $request ) ) {
499
                return false;
500
            }
501
502
            $res = maybe_unserialize( wp_remote_retrieve_body( $request ) );
503
504
            if ( ! is_object( $res ) && ! is_array( $res ) ) {
505
                return false;
506
            }
507
508
            return $res;
509
        }
510
511
        /**
512
         * Updates information on the "View version x.x details" page with custom data.
513
         *
514
         * @author Vova Feldman (@svovaf)
515
         * @since  1.0.4
516
         *
517
         * @uses   FS_Api
518
         *
519
         * @param object $data
520
         * @param string $action
521
         * @param mixed  $args
522
         *
523
         * @return object
524
         */
525
        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...
526
            $this->_logger->entrance();
527
528
            if ( ( 'plugin_information' !== $action ) ||
529
                 ! isset( $args->slug )
530
            ) {
531
                return $data;
532
            }
533
534
            $addon    = false;
535
            $is_addon = false;
536
537
            if ( $this->_fs->get_slug() !== $args->slug ) {
538
                $addon = $this->_fs->get_addon_by_slug( $args->slug );
539
540
                if ( ! is_object( $addon ) ) {
541
                    return $data;
542
                }
543
544
                $is_addon = true;
545
            }
546
547
            $plugin_in_repo = false;
548
            if ( ! $is_addon ) {
549
                // Try to fetch info from .org repository.
550
                $data = self::_fetch_plugin_info_from_repository( $action, $args );
551
552
                $plugin_in_repo = ( false !== $data );
553
            }
554
555
            if ( ! $plugin_in_repo ) {
556
                $data = $args;
557
558
                // Fetch as much as possible info from local files.
559
                $plugin_local_data = $this->_fs->get_plugin_data();
560
                $data->name        = $plugin_local_data['Name'];
561
                $data->author      = $plugin_local_data['Author'];
562
                $data->sections    = array(
563
                    'description' => 'Upgrade ' . $plugin_local_data['Name'] . ' to latest.',
564
                );
565
566
                // @todo Store extra plugin info on Freemius or parse readme.txt markup.
567
                /*$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...
568
569
if ( !isset($info->error) ) {
570
    $data = $info;
571
}*/
572
            }
573
574
            // Get plugin's newest update.
575
            $new_version = $this->get_latest_download_details( $is_addon ? $addon->id : false );
576
577
            if ( ! is_object( $new_version ) || empty( $new_version->version ) ) {
578
                $data->version = $this->_fs->get_plugin_version();
579
            } else {
580
                if ( $is_addon ) {
581
                    $data->name    = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
582
                    $data->slug    = $addon->slug;
583
                    $data->url     = WP_FS__ADDRESS;
584
                    $data->package = $new_version->url;
585
                }
586
587
                if ( ! $plugin_in_repo ) {
588
                    $data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created;
589
                    $data->requires     = $new_version->requires_platform_version;
590
                    $data->tested       = $new_version->tested_up_to_version;
591
                }
592
593
                $data->version       = $new_version->version;
594
                $data->download_link = $new_version->url;
595
            }
596
597
            return $data;
598
        }
599
600
        /**
601
         * @author Vova Feldman (@svovaf)
602
         * @since  1.2.1.7
603
         *
604
         * @param number|bool $addon_id
605
         *
606
         * @return object
607
         */
608
        private function get_latest_download_details( $addon_id = false ) {
609
            return $this->_fs->_fetch_latest_version( $addon_id );
610
        }
611
612
        /**
613
         * Checks if a given basename has a matching folder name
614
         * with the current context plugin.
615
         *
616
         * @author Vova Feldman (@svovaf)
617
         * @since  1.2.1.6
618
         *
619
         * @param string $basename Current plugin's basename.
620
         *
621
         * @return bool
622
         */
623
        private function is_correct_folder_name( $basename = '' ) {
624
            if ( empty( $basename ) ) {
625
                $basename = $this->_fs->get_plugin_basename();
626
            }
627
628
            return ( $this->_fs->get_target_folder_name() != trim( dirname( $basename ), '/\\' ) );
629
        }
630
631
        /**
632
         * This is a special after upgrade handler for migrating modules
633
         * that didn't use the '-premium' suffix folder structure before
634
         * the migration.
635
         *
636
         * @author Vova Feldman (@svovaf)
637
         * @since  1.2.1.6
638
         *
639
         * @param bool  $response   Install response.
640
         * @param array $hook_extra Extra arguments passed to hooked filters.
641
         * @param array $result     Installation result data.
642
         *
643
         * @return bool
644
         */
645
        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...
646
            $basename = $this->_fs->get_plugin_basename();
647
648
            if ( true !== $response ||
649
                 empty( $hook_extra ) ||
650
                 empty( $hook_extra['plugin'] ) ||
651
                 $basename !== $hook_extra['plugin']
652
            ) {
653
                return $response;
654
            }
655
656
            $active_plugins_basenames = get_option( 'active_plugins' );
657
658
            for ( $i = 0, $len = count( $active_plugins_basenames ); $i < $len; $i ++ ) {
659
                if ( $basename === $active_plugins_basenames[ $i ] ) {
660
                    // Get filename including extension.
661
                    $filename = basename( $basename );
662
663
                    $new_basename = plugin_basename(
664
                        trailingslashit( $this->_fs->get_slug() . ( $this->_fs->is_premium() ? '-premium' : '' ) ) .
665
                        $filename
666
                    );
667
668
                    // Verify that the expected correct path exists.
669
                    if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $new_basename ) ) ) {
670
                        // Override active plugin name.
671
                        $active_plugins_basenames[ $i ] = $new_basename;
672
                        update_option( 'active_plugins', $active_plugins_basenames );
673
                    }
674
675
                    break;
676
                }
677
            }
678
679
            return $response;
680
        }
681
682
        #----------------------------------------------------------------------------------
683
        #region Auto Activation
684
        #----------------------------------------------------------------------------------
685
686
        /**
687
         * Installs and active a plugin when explicitly requested that from a 3rd party service.
688
         *
689
         * This logic was inspired by the TGMPA GPL licensed library by Thomas Griffin.
690
         *
691
         * @link   http://tgmpluginactivation.com/
692
         *
693
         * @author Vova Feldman
694
         * @since  1.2.1.7
695
         *
696
         * @link   https://make.wordpress.org/plugins/2017/03/16/clarification-of-guideline-8-executable-code-and-installs/
697
         *
698
         * @uses   WP_Filesystem
699
         * @uses   WP_Error
700
         * @uses   WP_Upgrader
701
         * @uses   Plugin_Upgrader
702
         * @uses   Plugin_Installer_Skin
703
         * @uses   Plugin_Upgrader_Skin
704
         *
705
         * @param number|bool $plugin_id
706
         *
707
         * @return array
708
         */
709
        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...
710
            if ( ! empty( $plugin_id ) && ! FS_Plugin::is_valid_id( $plugin_id ) ) {
711
                // Invalid plugin ID.
712
                return array(
713
                    'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
714
                    'code'    => 'invalid_module_id',
715
                );
716
            }
717
718
            $is_addon = false;
719
            if ( FS_Plugin::is_valid_id( $plugin_id ) &&
720
                 $plugin_id != $this->_fs->get_id()
721
            ) {
722
                $addon = $this->_fs->get_addon( $plugin_id );
723
724
                if ( ! is_object( $addon ) ) {
725
                    // Invalid add-on ID.
726
                    return array(
727
                        'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
728
                        'code'    => 'invalid_module_id',
729
                    );
730
                }
731
732
                $slug  = $addon->slug;
733
                $title = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
734
735
                $is_addon = true;
736
            } else {
737
                $slug  = $this->_fs->get_slug();
738
                $title = $this->_fs->get_plugin_title() .
739
                         ( $this->_fs->is_addon() ? ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ) : '' );
740
            }
741
742
            if ( $this->is_premium_plugin_active( $plugin_id ) ) {
743
                // Premium version already activated.
744
                return array(
745
                    'message' => $is_addon ?
746
                        $this->_fs->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ) :
747
                        $this->_fs->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ),
748
                    'code'    => 'premium_installed',
749
                );
750
            }
751
752
            $latest_version = $this->get_latest_download_details( $plugin_id );
753
            $target_folder  = "{$slug}-premium";
754
755
            // Prep variables for Plugin_Installer_Skin class.
756
            $extra         = array();
757
            $extra['slug'] = $target_folder;
758
            $source        = $latest_version->url;
759
            $api           = null;
760
761
            $install_url = add_query_arg(
762
                array(
763
                    'action' => 'install-plugin',
764
                    'plugin' => urlencode( $slug ),
765
                ),
766
                'update.php'
767
            );
768
769
            if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
770
                // Include required resources for the installation.
771
                require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
772
            }
773
774
            $skin_args = array(
775
                'type'   => 'web',
776
                'title'  => sprintf( $this->_fs->get_text_inline( 'Installing plugin: %s', 'installing-plugin-x' ), $title ),
777
                'url'    => esc_url_raw( $install_url ),
778
                'nonce'  => 'install-plugin_' . $slug,
779
                'plugin' => '',
780
                'api'    => $api,
781
                'extra'  => $extra,
782
            );
783
784
//			$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...
785
//			$skin = new Plugin_Installer_Skin( $skin_args );
786
            $skin = new WP_Ajax_Upgrader_Skin( $skin_args );
787
788
            // Create a new instance of Plugin_Upgrader.
789
            $upgrader = new Plugin_Upgrader( $skin );
790
791
            // Perform the action and install the plugin from the $source urldecode().
792
            add_filter( 'upgrader_source_selection', array( &$this, '_maybe_adjust_source_dir' ), 1, 3 );
793
794
            $install_result = $upgrader->install( $source );
795
796
            remove_filter( 'upgrader_source_selection', array( &$this, '_maybe_adjust_source_dir' ), 1 );
797
798
            if ( is_wp_error( $install_result ) ) {
799
                return array(
800
                    'message' => $install_result->get_error_message(),
801
                    'code'    => $install_result->get_error_code(),
802
                );
803
            } elseif ( is_wp_error( $skin->result ) ) {
804
                return array(
805
                    'message' => $skin->result->get_error_message(),
806
                    'code'    => $skin->result->get_error_code(),
807
                );
808
            } elseif ( $skin->get_errors()->get_error_code() ) {
809
                return array(
810
                    'message' => $skin->get_error_messages(),
811
                    'code'    => 'unknown',
812
                );
813
            } elseif ( is_null( $install_result ) ) {
814
                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...
815
816
                $error_code    = 'unable_to_connect_to_filesystem';
817
                $error_message = $this->_fs->get_text_inline( 'Unable to connect to the filesystem. Please confirm your credentials.' );
818
819
                // Pass through the error from WP_Filesystem if one was raised.
820
                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...
821
                     is_wp_error( $wp_filesystem->errors ) &&
822
                     $wp_filesystem->errors->get_error_code()
823
                ) {
824
                    $error_message = $wp_filesystem->errors->get_error_message();
825
                }
826
827
                return array(
828
                    'message' => $error_message,
829
                    'code'    => $error_code,
830
                );
831
            }
832
833
            // Grab the full path to the main plugin's file.
834
            $plugin_activate = $upgrader->plugin_info();
835
836
            // Try to activate the plugin.
837
            $activation_result = $this->try_activate_plugin( $plugin_activate );
838
839
            if ( is_wp_error( $activation_result ) ) {
840
                return array(
841
                    'message' => $activation_result->get_error_message(),
842
                    'code'    => $activation_result->get_error_code(),
843
                );
844
            }
845
846
            return $skin->get_upgrade_messages();
847
        }
848
849
        /**
850
         * Tries to activate a plugin. If fails, returns the error.
851
         *
852
         * @author Vova Feldman
853
         * @since  1.2.1.7
854
         *
855
         * @param string $file_path Path within wp-plugins/ to main plugin file.
856
         *                          This determines the styling of the output messages.
857
         *
858
         * @return bool|WP_Error
859
         */
860
        protected function try_activate_plugin( $file_path ) {
861
            $activate = activate_plugin( $file_path, '', $this->_fs->is_network_active() );
862
863
            return is_wp_error( $activate ) ?
864
                $activate :
865
                true;
866
        }
867
868
        /**
869
         * Check if a premium module version is already active.
870
         *
871
         * @author Vova Feldman
872
         * @since  1.2.1.7
873
         *
874
         * @param number|bool $plugin_id
875
         *
876
         * @return bool
877
         */
878
        private function is_premium_plugin_active( $plugin_id = false ) {
879
            if ( $plugin_id != $this->_fs->get_id() ) {
880
                return $this->_fs->is_addon_activated( $plugin_id, true );
881
            }
882
883
            return is_plugin_active( $this->_fs->premium_plugin_basename() );
884
        }
885
886
        /**
887
         * Adjust the plugin directory name if necessary.
888
         * Assumes plugin has a folder (not a single file plugin).
889
         *
890
         * The final destination directory of a plugin is based on the subdirectory name found in the
891
         * (un)zipped source. In some cases this subdirectory name is not the same as the expected
892
         * slug and the plugin will not be recognized as installed. This is fixed by adjusting
893
         * the temporary unzipped source subdirectory name to the expected plugin slug.
894
         *
895
         * @author Vova Feldman
896
         * @since  1.2.1.7
897
         *
898
         * @param string       $source        Path to upgrade/zip-file-name.tmp/subdirectory/.
899
         * @param string       $remote_source Path to upgrade/zip-file-name.tmp.
900
         * @param \WP_Upgrader $upgrader      Instance of the upgrader which installs the plugin.
901
         *
902
         * @return string|WP_Error
903
         */
904
        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...
905
            if ( ! is_object( $GLOBALS['wp_filesystem'] ) ) {
906
                return $source;
907
            }
908
909
            // Figure out what the slug is supposed to be.
910
            $desired_slug = $upgrader->skin->options['extra']['slug'];
911
912
            $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
913
914
            if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
915
                $from_path = untrailingslashit( $source );
916
                $to_path   = trailingslashit( $remote_source ) . $desired_slug;
917
918
                if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) {
919
                    return trailingslashit( $to_path );
920
                } else {
921
                    return new WP_Error(
922
                        'rename_failed',
923
                        $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' ),
924
                        array(
925
                            'found'    => $subdir_name,
926
                            'expected' => $desired_slug
927
                        ) );
928
                }
929
            }
930
931
            return $source;
932
        }
933
934
        #endregion
935
    }