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 ( b3ee5f...a83cd4 )
by Brad
06:08 queued 03:01
created

FS_Plugin_Updater::_maybe_adjust_source_dir()   C

Complexity

Conditions 12
Paths 32

Size

Total Lines 81

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
nc 32
nop 3
dl 0
loc 81
rs 5.9878
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
         * @var array
34
         * @since 2.1.2
35
         */
36
        private $_translation_updates;
37
38
        private static $_upgrade_basename = null;
39
40
        #--------------------------------------------------------------------------------
41
        #region Singleton
42
        #--------------------------------------------------------------------------------
43
44
        /**
45
         * @var FS_Plugin_Updater[]
46
         * @since 2.0.0
47
         */
48
        private static $_INSTANCES = array();
49
50
        /**
51
         * @param Freemius $freemius
52
         *
53
         * @return FS_Plugin_Updater
54
         */
55
        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...
56
            $key = $freemius->get_id();
57
58
            if ( ! isset( self::$_INSTANCES[ $key ] ) ) {
59
                self::$_INSTANCES[ $key ] = new self( $freemius );
60
            }
61
62
            return self::$_INSTANCES[ $key ];
63
        }
64
65
        #endregion
66
67
        private function __construct( Freemius $freemius ) {
68
            $this->_fs = $freemius;
69
70
            $this->_logger = FS_Logger::get_logger( WP_FS__SLUG . '_' . $freemius->get_slug() . '_updater', WP_FS__DEBUG_SDK, WP_FS__ECHO_DEBUG_SDK );
71
72
            $this->filters();
73
        }
74
75
        /**
76
         * Initiate required filters.
77
         *
78
         * @author Vova Feldman (@svovaf)
79
         * @since  1.0.4
80
         */
81
        private function filters() {
82
            // Override request for plugin information
83
            add_filter( 'plugins_api', array( &$this, 'plugins_api_filter' ), 10, 3 );
84
85
            $this->add_transient_filters();
86
87
            /**
88
             * If user has the premium plugin's code but do NOT have an active license,
89
             * encourage him to upgrade by showing that there's a new release, but instead
90
             * of showing an update link, show upgrade link to the pricing page.
91
             *
92
             * @since 1.1.6
93
             *
94
             */
95
            // WP 2.9+
96
            add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array(
97
                &$this,
98
                'catch_plugin_update_row'
99
            ), 9 );
100
            add_action( "after_plugin_row_{$this->_fs->get_plugin_basename()}", array(
101
                &$this,
102
                'edit_and_echo_plugin_update_row'
103
            ), 11, 2 );
104
105
            add_action( 'admin_head', array( &$this, 'catch_plugin_information_dialog_contents' ) );
106
107
            if ( ! WP_FS__IS_PRODUCTION_MODE ) {
108
                add_filter( 'http_request_host_is_external', array(
109
                    $this,
110
                    'http_request_host_is_external_filter'
111
                ), 10, 3 );
112
            }
113
114
            if ( $this->_fs->is_premium() ) {
115
                if ( ! $this->is_correct_folder_name() ) {
116
                    add_filter( 'upgrader_post_install', array( &$this, '_maybe_update_folder_name' ), 10, 3 );
117
                }
118
119
                add_filter( 'upgrader_pre_install', array( 'FS_Plugin_Updater', '_store_basename_for_source_adjustment' ), 1, 2 );
120
                add_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1, 3 );
121
122
                if ( ! $this->_fs->has_any_active_valid_license() ) {
123
                    add_filter( 'wp_prepare_themes_for_js', array( &$this, 'change_theme_update_info_html' ), 10, 1 );
124
                }
125
            }
126
        }
127
128
        /**
129
         * @author Leo Fajardo (@leorw)
130
         * @since 2.1.4
131
         */
132
        function catch_plugin_information_dialog_contents() {
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...
133
            if (
134
                'plugin-information' !== fs_request_get( 'tab', false ) ||
135
                $this->_fs->get_slug() !== fs_request_get( 'plugin', false )
136
            ) {
137
                return;
138
            }
139
140
            add_action( 'admin_footer', array( &$this, 'edit_and_echo_plugin_information_dialog_contents' ), 0, 1 );
141
142
            ob_start();
143
        }
144
145
        /**
146
         * @author Leo Fajardo (@leorw)
147
         * @since 2.1.4
148
         *
149
         * @param string $hook_suffix
150
         */
151
        function edit_and_echo_plugin_information_dialog_contents( $hook_suffix ) {
0 ignored issues
show
Unused Code introduced by
The parameter $hook_suffix 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...
152
            if (
153
                'plugin-information' !== fs_request_get( 'tab', false ) ||
154
                $this->_fs->get_slug() !== fs_request_get( 'plugin', false )
155
            ) {
156
                return;
157
            }
158
159
            $license = $this->_fs->_get_license();
160
161
            $subscription = ( is_object( $license ) && ! $license->is_lifetime() ) ?
162
                $this->_fs->_get_subscription( $license->id ) :
163
                null;
164
165
            $contents = ob_get_clean();
166
167
            /**
168
             * Replace the plugin information dialog's "Install Update Now" button's text and URL. If there's a license,
169
             * the text will be "Renew license" and will link to the checkout page with the license's billing cycle
170
             * and quota. If there's no license, the text will be "Buy license" and will link to the pricing page.
171
             */
172
            $contents = preg_replace(
173
                '/(.+\<a.+)(id="plugin_update_from_iframe")(.+href=")([^\s]+)(".+\>)(.+)(\<\/a.+)/is',
174
                is_object( $license ) ?
175
                    sprintf(
176
                        '$1$3%s$5%s$7',
177
                        $this->_fs->checkout_url(
178
                            is_object( $subscription ) ?
179
                                ( 1 == $subscription->billing_cycle ? WP_FS__PERIOD_MONTHLY : WP_FS__PERIOD_ANNUALLY ) :
180
                                WP_FS__PERIOD_LIFETIME,
181
                            false,
182
                            array( 'licenses' => $license->quota )
183
                        ),
184
                        fs_text_inline( 'Renew license', 'renew-license', $this->_fs->get_slug() )
185
                    ) :
186
                    sprintf(
187
                        '$1$3%s$5%s$7',
188
                        $this->_fs->pricing_url(),
189
                        fs_text_inline( 'Buy license', 'buy-license', $this->_fs->get_slug() )
190
                    ),
191
                $contents
192
            );
193
194
            echo $contents;
195
        }
196
197
        /**
198
         * @author Vova Feldman (@svovaf)
199
         * @since  2.0.0
200
         */
201
        private function add_transient_filters() {
202
            add_filter( 'pre_set_site_transient_update_plugins', array(
203
                &$this,
204
                'pre_set_site_transient_update_plugins_filter'
205
            ) );
206
207
            add_filter( 'pre_set_site_transient_update_themes', array(
208
                &$this,
209
                'pre_set_site_transient_update_plugins_filter'
210
            ) );
211
        }
212
213
        /**
214
         * @author Vova Feldman (@svovaf)
215
         * @since  2.0.0
216
         */
217
        private function remove_transient_filters() {
218
            remove_filter( 'pre_set_site_transient_update_plugins', array(
219
                &$this,
220
                'pre_set_site_transient_update_plugins_filter'
221
            ) );
222
223
            remove_filter( 'pre_set_site_transient_update_themes', array(
224
                &$this,
225
                'pre_set_site_transient_update_plugins_filter'
226
            ) );
227
        }
228
229
        /**
230
         * Capture plugin update row by turning output buffering.
231
         *
232
         * @author Vova Feldman (@svovaf)
233
         * @since  1.1.6
234
         */
235
        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...
236
            ob_start();
237
        }
238
239
        /**
240
         * Overrides default update message format with "renew your license" message.
241
         *
242
         * @author Vova Feldman (@svovaf)
243
         * @since  1.1.6
244
         *
245
         * @param string $file
246
         * @param array  $plugin_data
247
         */
248
        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...
249
            $plugin_update_row = ob_get_clean();
250
251
            $current = get_site_transient( 'update_plugins' );
252
            if ( ! isset( $current->response[ $file ] ) ) {
253
                echo $plugin_update_row;
254
255
                return;
256
            }
257
258
            $r = $current->response[ $file ];
259
260
            if ( ! $this->_fs->has_any_active_valid_license() ) {
261
                /**
262
                 * Turn the "new version" text into a link that opens the plugin information dialog when clicked and
263
                 * make the "View version x details" text link to the checkout page instead of opening the plugin
264
                 * information dialog when clicked.
265
                 *
266
                 * Sample input:
267
                 *      There is a new version of Awesome Plugin available. <a href="...>View version x.y.z details</a> or <a href="...>update now</a>.
268
                 * Output:
269
                 *      There is a <a href="...>new version</a> of Awesome Plugin available. <a href="...>Buy a license now</a> to access version x.y.z security & feature updates, and support.
270
                 *
271
                 * @author Leo Fajardo (@leorw)
272
                 */
273
                $plugin_update_row = preg_replace(
274
                    '/(\<div.+>)(.+)(\<a.+href="([^\s]+)"([^\<]+)\>.+\<a.+)(\<\/div\>)/is',
275
                    (
276
                        '$1' .
277
                        sprintf(
278
                            fs_text_inline( 'There is a %s of %s available.', 'new-version-available', $this->_fs->get_slug() ),
279
                            sprintf(
280
                                '<a href="$4"%s>%s</a>',
281
                                '$5',
282
                                fs_text_inline( 'new version', 'new-version', $this->_fs->get_slug() )
283
                            ),
284
                            $this->_fs->get_plugin_title()
285
                        ) .
286
                        ' ' .
287
                        $this->_fs->version_upgrade_checkout_link( $r->new_version ) .
288
                        '$6'
289
                    ),
290
                    $plugin_update_row
291
                );
292
            }
293
294
            if (
295
                $this->_fs->is_plugin() &&
296
                isset( $r->upgrade_notice ) &&
297
                strlen( trim( $r->upgrade_notice ) ) > 0
298
            ) {
299
                $upgrade_notice_html = sprintf(
300
                    '<p class="notice upgrade-notice"><strong>%s</strong> %s</p>',
301
                    fs_text_inline( 'Important Upgrade Notice:', 'upgrade_notice', $this->_fs->get_slug() ),
302
                    esc_html( $r->upgrade_notice )
303
                );
304
305
                $plugin_update_row = str_replace( '</div>', '</div>' . $upgrade_notice_html, $plugin_update_row );
306
            }
307
308
            echo $plugin_update_row;
309
        }
310
311
        /**
312
         * @author Leo Fajardo (@leorw)
313
         * @since  2.0.2
314
         *
315
         * @param array $prepared_themes
316
         *
317
         * @return array
318
         */
319
        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...
320
            $theme_basename = $this->_fs->get_plugin_basename();
321
322
            if ( ! isset( $prepared_themes[ $theme_basename ] ) ) {
323
                return $prepared_themes;
324
            }
325
326
            $themes_update = get_site_transient( 'update_themes' );
327
            if ( ! isset( $themes_update->response[ $theme_basename ] ) ||
328
                empty( $themes_update->response[ $theme_basename ]['package'] )
329
            ) {
330
                return $prepared_themes;
331
            }
332
333
            $prepared_themes[ $theme_basename ]['update'] = preg_replace(
334
                '/(\<p.+>)(.+)(\<a.+\<a.+)\.(.+\<\/p\>)/is',
335
                '$1 $2 ' . $this->_fs->version_upgrade_checkout_link( $themes_update->response[ $theme_basename ]['new_version'] ) .
336
                '$4',
337
                $prepared_themes[ $theme_basename ]['update']
338
            );
339
340
            // Set to false to prevent the "Update now" link for the context theme from being shown on the "Themes" page.
341
            $prepared_themes[ $theme_basename ]['hasPackage'] = false;
342
343
            return $prepared_themes;
344
        }
345
346
        /**
347
         * Since WP version 3.6, a new security feature was added that denies access to repository with a local ip.
348
         * During development mode we want to be able updating plugin versions via our localhost repository. This
349
         * filter white-list all domains including "api.freemius".
350
         *
351
         * @link   http://www.emanueletessore.com/wordpress-download-failed-valid-url-provided/
352
         *
353
         * @author Vova Feldman (@svovaf)
354
         * @since  1.0.4
355
         *
356
         * @param bool   $allow
357
         * @param string $host
358
         * @param string $url
359
         *
360
         * @return bool
361
         */
362
        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...
363
            return ( false !== strpos( $host, 'freemius' ) ) ? true : $allow;
364
        }
365
366
        /**
367
         * Check for Updates at the defined API endpoint and modify the update array.
368
         *
369
         * This function dives into the update api just when WordPress creates its update array,
370
         * then adds a custom API call and injects the custom plugin data retrieved from the API.
371
         * It is reassembled from parts of the native WordPress plugin update code.
372
         * See wp-includes/update.php line 121 for the original wp_update_plugins() function.
373
         *
374
         * @author Vova Feldman (@svovaf)
375
         * @since  1.0.4
376
         *
377
         * @uses   FS_Api
378
         *
379
         * @param object $transient_data Update array build by WordPress.
380
         *
381
         * @return object Modified update array with custom plugin data.
382
         */
383
        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...
384
            $this->_logger->entrance();
385
386
            /**
387
             * "plugins" or "themes".
388
             *
389
             * @author Leo Fajardo (@leorw)
390
             * @since  1.2.2
391
             */
392
            $module_type = $this->_fs->get_module_type() . 's';
393
394
            /**
395
             * Ensure that we don't mix plugins update info with themes update info.
396
             *
397
             * @author Leo Fajardo (@leorw)
398
             * @since  1.2.2
399
             */
400
            if ( "pre_set_site_transient_update_{$module_type}" !== current_filter() ) {
401
                return $transient_data;
402
            }
403
404
            if ( empty( $transient_data ) ||
405
                 defined( 'WP_FS__UNINSTALL_MODE' )
406
            ) {
407
                return $transient_data;
408
            }
409
410
            if ( ! isset( $this->_update_details ) ) {
411
                // Get plugin's newest update.
412
                $new_version = $this->_fs->get_update(
413
                    false,
414
                    fs_request_get_bool( 'force-check' ),
415
                    WP_FS__TIME_24_HOURS_IN_SEC / 24,
416
                    $this->_fs->get_plugin_version()
417
                );
418
419
                $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...
420
421
                if ( is_object( $new_version ) ) {
422
                    $this->_logger->log( 'Found newer plugin version ' . $new_version->version );
423
424
                    /**
425
                     * Cache plugin details locally since set_site_transient( 'update_plugins' )
426
                     * called multiple times and the non wp.org plugins are filtered after the
427
                     * call to .org.
428
                     *
429
                     * @since 1.1.8.1
430
                     */
431
                    $this->_update_details = $this->get_update_details( $new_version );
432
                }
433
            }
434
435
            if ( is_object( $this->_update_details ) ) {
436
                // Add plugin to transient data.
437
                $transient_data->response[ $this->_fs->get_plugin_basename() ] = $this->_fs->is_plugin() ?
438
                    $this->_update_details :
439
                    (array) $this->_update_details;
440
            }
441
442
            $slug = $this->_fs->get_slug();
443
444
            if ( $this->_fs->is_org_repo_compliant() && $this->_fs->is_freemium() ) {
445
                if ( ! isset( $this->_translation_updates ) ) {
446
                    $this->_translation_updates = array();
447
448
                    if ( current_user_can( 'update_languages' ) ) {
449
                        $translation_updates = $this->fetch_wp_org_module_translation_updates( $module_type, $slug );
450
                        if ( ! empty( $translation_updates ) ) {
451
                            $this->_translation_updates = $translation_updates;
452
                        }
453
                    }
454
                }
455
456
                if ( ! empty( $this->_translation_updates ) ) {
457
                    $all_translation_updates = ( isset( $transient_data->translations ) && is_array( $transient_data->translations ) ) ?
458
                        $transient_data->translations :
459
                        array();
460
461
                    $current_plugin_translation_updates_map = array();
462
                    foreach ( $all_translation_updates as $key => $translation_update ) {
463
                        if ( $module_type === ( $translation_update['type'] . 's' ) && $slug === $translation_update['slug'] ) {
464
                            $current_plugin_translation_updates_map[ $translation_update['language'] ] = $translation_update;
465
                            unset( $all_translation_updates[ $key ] );
466
                        }
467
                    }
468
469
                    foreach ( $this->_translation_updates as $translation_update ) {
470
                        $lang = $translation_update['language'];
471
                        if ( ! isset( $current_plugin_translation_updates_map[ $lang ] ) ||
472
                            version_compare( $translation_update['version'], $current_plugin_translation_updates_map[ $lang ]['version'], '>' )
473
                        ) {
474
                            $current_plugin_translation_updates_map[ $lang ] = $translation_update;
475
                        }
476
                    }
477
478
                    $transient_data->translations = array_merge( $all_translation_updates, array_values( $current_plugin_translation_updates_map ) );
479
                }
480
            }
481
482
            return $transient_data;
483
        }
484
485
        /**
486
         * Get module's required data for the updates mechanism.
487
         *
488
         * @author Vova Feldman (@svovaf)
489
         * @since  2.0.0
490
         *
491
         * @param \FS_Plugin_Tag $new_version
492
         *
493
         * @return object
494
         */
495
        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...
496
            $update              = new stdClass();
497
            $update->slug        = $this->_fs->get_slug();
498
            $update->new_version = $new_version->version;
499
            $update->url         = WP_FS__ADDRESS;
500
            $update->package     = $new_version->url;
501
            $update->tested      = $new_version->tested_up_to_version;
502
            $update->requires    = $new_version->requires_platform_version;
503
504
            $icon = $this->_fs->get_local_icon_url();
505
506
            if ( ! empty( $icon ) ) {
507
                $update->icons = array(
508
//                    '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...
509
//                    '2x'      => $icon,
510
                    'default' => $icon,
511
                );
512
            }
513
514
            if ( $this->_fs->is_premium() ) {
515
                $latest_tag = $this->_fs->_fetch_latest_version( $this->_fs->get_id(), false );
516
517
                if (
518
                    isset( $latest_tag->readme ) &&
519
                    isset( $latest_tag->readme->upgrade_notice ) &&
520
                    ! empty( $latest_tag->readme->upgrade_notice )
521
                ) {
522
                    $update->upgrade_notice = $latest_tag->readme->upgrade_notice;
523
                }
524
            }
525
526
            $update->{$this->_fs->get_module_type()} = $this->_fs->get_plugin_basename();
527
528
            return $update;
529
        }
530
531
        /**
532
         * Update the updates transient with the module's update information.
533
         *
534
         * This method is required for multisite environment.
535
         * If a module is site activated (not network) and not on the main site,
536
         * the module will NOT be executed on the network level, therefore, the
537
         * custom updates logic will not be executed as well, so unless we force
538
         * the injection of the update into the updates transient, premium updates
539
         * will not work.
540
         *
541
         * @author Vova Feldman (@svovaf)
542
         * @since  2.0.0
543
         *
544
         * @param \FS_Plugin_Tag $new_version
545
         */
546
        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...
547
            $this->_logger->entrance();
548
549
            $transient_key = "update_{$this->_fs->get_module_type()}s";
550
551
            $transient_data = get_site_transient( $transient_key );
552
553
            $transient_data = is_object( $transient_data ) ?
554
                $transient_data :
555
                new stdClass();
556
557
            // Alias.
558
            $basename  = $this->_fs->get_plugin_basename();
559
            $is_plugin = $this->_fs->is_plugin();
560
561
            if ( ! isset( $transient_data->response ) ||
562
                 ! is_array( $transient_data->response )
563
            ) {
564
                $transient_data->response = array();
565
            } else if ( ! empty( $transient_data->response[ $basename ] ) ) {
566
                $version = $is_plugin ?
567
                    ( ! empty( $transient_data->response[ $basename ]->new_version ) ?
568
                        $transient_data->response[ $basename ]->new_version :
569
                        null
570
                    ) : ( ! empty( $transient_data->response[ $basename ]['new_version'] ) ?
571
                        $transient_data->response[ $basename ]['new_version'] :
572
                        null
573
                    );
574
575
                if ( $version == $new_version->version ) {
576
                    // The update data is already set.
577
                    return;
578
                }
579
            }
580
581
            // Remove the added filters.
582
            $this->remove_transient_filters();
583
584
            $this->_update_details = $this->get_update_details( $new_version );
585
586
            // Set update data in transient.
587
            $transient_data->response[ $basename ] = $is_plugin ?
588
                $this->_update_details :
589
                (array) $this->_update_details;
590
591
            if ( ! isset( $transient_data->checked ) ||
592
                 ! is_array( $transient_data->checked )
593
            ) {
594
                $transient_data->checked = array();
595
            }
596
597
            // Flag the module as if it was already checked.
598
            $transient_data->checked[ $basename ] = $this->_fs->get_plugin_version();
599
            $transient_data->last_checked         = time();
600
601
            set_site_transient( $transient_key, $transient_data );
602
603
            $this->add_transient_filters();
604
        }
605
606
        /**
607
         * @author Leo Fajardo (@leorw)
608
         * @since 2.0.2
609
         */
610
        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...
611
            $this->_logger->entrance();
612
613
            $transient_key = "update_{$this->_fs->get_module_type()}s";
614
615
            $transient_data = get_site_transient( $transient_key );
616
617
            // Alias
618
            $basename = $this->_fs->get_plugin_basename();
619
620
            if ( ! is_object( $transient_data ) ||
621
                ! isset( $transient_data->response ) ||
622
                 ! is_array( $transient_data->response ) ||
623
                empty( $transient_data->response[ $basename ] )
624
            ) {
625
                return;
626
            }
627
628
            unset( $transient_data->response[ $basename ] );
629
630
            // Remove the added filters.
631
            $this->remove_transient_filters();
632
633
            set_site_transient( $transient_key, $transient_data );
634
635
            $this->add_transient_filters();
636
        }
637
638
        /**
639
         * Try to fetch plugin's info from .org repository.
640
         *
641
         * @author Vova Feldman (@svovaf)
642
         * @since  1.0.5
643
         *
644
         * @param string $action
645
         * @param object $args
646
         *
647
         * @return bool|mixed
648
         */
649
        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...
650
            $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...
651
            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...
652
                $url = set_url_scheme( $url, 'https' );
653
            }
654
655
            $args = array(
656
                'timeout' => 15,
657
                'body'    => array(
658
                    'action'  => $action,
659
                    'request' => serialize( $args )
660
                )
661
            );
662
663
            $request = wp_remote_post( $url, $args );
664
665
            if ( is_wp_error( $request ) ) {
666
                return false;
667
            }
668
669
            $res = maybe_unserialize( wp_remote_retrieve_body( $request ) );
670
671
            if ( ! is_object( $res ) && ! is_array( $res ) ) {
672
                return false;
673
            }
674
675
            return $res;
676
        }
677
678
        /**
679
         * Fetches module translation updates from wordpress.org.
680
         *
681
         * @author Leo Fajardo (@leorw)
682
         * @since  2.1.2
683
         *
684
         * @param string $module_type
685
         * @param string $slug
686
         *
687
         * @return array|null
688
         */
689
        private function fetch_wp_org_module_translation_updates( $module_type, $slug ) {
690
            $plugin_data = $this->_fs->get_plugin_data();
691
692
            $locales = array_values( get_available_languages() );
693
            $locales = apply_filters( "{$module_type}_update_check_locales", $locales );
694
            $locales = array_unique( $locales );
695
696
            $plugin_basename = $this->_fs->get_plugin_basename();
697
            if ( 'themes' === $module_type ) {
698
                $plugin_basename = $slug;
699
            }
700
701
            global $wp_version;
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...
702
703
            $request_args = array(
704
                'timeout' => 15,
705
                'body'    => array(
706
                    "{$module_type}" => json_encode(
707
                        array(
708
                            "{$module_type}" => array(
709
                                $plugin_basename => array(
710
                                    'Name'   => trim( str_replace( $this->_fs->get_plugin()->premium_suffix, '', $plugin_data['Name'] ) ),
711
                                    'Author' => $plugin_data['Author'],
712
                                )
713
                            )
714
                        )
715
                    ),
716
                    'translations'    => json_encode( $this->get_installed_translations( $module_type, $slug ) ),
717
                    'locale'          => json_encode( $locales )
718
                ),
719
                'user-agent' => ( 'WordPress/' . $wp_version . '; ' . home_url( '/' ) )
720
            );
721
722
            $url = "http://api.wordpress.org/{$module_type}/update-check/1.1/";
723
            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...
724
                $url = set_url_scheme( $url, 'https' );
725
            }
726
727
            $raw_response = Freemius::safe_remote_post(
728
                $url,
729
                $request_args,
730
                WP_FS__TIME_24_HOURS_IN_SEC,
731
                WP_FS__TIME_12_HOURS_IN_SEC,
732
                false
733
            );
734
735
            if ( is_wp_error( $raw_response ) ) {
736
                return null;
737
            }
738
739
            $response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
740
741
            if ( ! is_array( $response ) ) {
742
                return null;
743
            }
744
745
            if ( ! isset( $response['translations'] ) || empty( $response['translations'] ) ) {
746
                return null;
747
            }
748
749
            return $response['translations'];
750
        }
751
752
        /**
753
         * @author Leo Fajardo (@leorw)
754
         * @since 2.1.2
755
         *
756
         * @param string $module_type
757
         * @param string $slug
758
         *
759
         * @return array
760
         */
761
        private function get_installed_translations( $module_type, $slug ) {
762
            if ( function_exists( 'wp_get_installed_translations' ) ) {
763
                return wp_get_installed_translations( $module_type );
764
            }
765
766
            $dir = "/{$module_type}";
767
768
            if ( ! is_dir( WP_LANG_DIR . $dir ) )
769
                return array();
770
771
            $files = scandir( WP_LANG_DIR . $dir );
772
            if ( ! $files )
0 ignored issues
show
Bug Best Practice introduced by
The expression $files of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
773
                return array();
774
775
            $language_data = array();
776
777
            foreach ( $files as $file ) {
778
                if ( 0 !== strpos( $file, $slug ) ) {
779
                    continue;
780
                }
781
782
                if ( '.' === $file[0] || is_dir( WP_LANG_DIR . "{$dir}/{$file}" ) ) {
783
                    continue;
784
                }
785
786
                if ( substr( $file, -3 ) !== '.po' ) {
787
                    continue;
788
                }
789
790
                if ( ! preg_match( '/(?:(.+)-)?([a-z]{2,3}(?:_[A-Z]{2})?(?:_[a-z0-9]+)?).po/', $file, $match ) ) {
791
                    continue;
792
                }
793
794
                if ( ! in_array( substr( $file, 0, -3 ) . '.mo', $files ) )  {
795
                    continue;
796
                }
797
798
                list( , $textdomain, $language ) = $match;
799
800
                if ( '' === $textdomain ) {
801
                    $textdomain = 'default';
802
                }
803
804
                $language_data[ $textdomain ][ $language ] = wp_get_pomo_file_data( WP_LANG_DIR . "{$dir}/{$file}" );
805
            }
806
807
            return $language_data;
808
        }
809
810
        /**
811
         * Updates information on the "View version x.x details" page with custom data.
812
         *
813
         * @author Vova Feldman (@svovaf)
814
         * @since  1.0.4
815
         *
816
         * @uses   FS_Api
817
         *
818
         * @param object $data
819
         * @param string $action
820
         * @param mixed  $args
821
         *
822
         * @return object
823
         */
824
        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...
825
            $this->_logger->entrance();
826
827
            if ( ( 'plugin_information' !== $action ) ||
828
                 ! isset( $args->slug )
829
            ) {
830
                return $data;
831
            }
832
833
            $addon    = false;
834
            $is_addon = false;
835
836
            if ( $this->_fs->get_slug() !== $args->slug ) {
837
                $addon = $this->_fs->get_addon_by_slug( $args->slug );
838
839
                if ( ! is_object( $addon ) ) {
840
                    return $data;
841
                }
842
843
                $is_addon = true;
844
            }
845
846
            $plugin_in_repo = false;
847
            if ( ! $is_addon ) {
848
                // Try to fetch info from .org repository.
849
                $data = self::_fetch_plugin_info_from_repository( $action, $args );
850
851
                $plugin_in_repo = ( false !== $data );
852
            }
853
854
            if ( ! $plugin_in_repo ) {
855
                $data = $args;
856
857
                // Fetch as much as possible info from local files.
858
                $plugin_local_data = $this->_fs->get_plugin_data();
859
                $data->name        = $plugin_local_data['Name'];
860
                $data->author      = $plugin_local_data['Author'];
861
                $data->sections    = array(
862
                    'description' => 'Upgrade ' . $plugin_local_data['Name'] . ' to latest.',
863
                );
864
865
                // @todo Store extra plugin info on Freemius or parse readme.txt markup.
866
                /*$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...
867
868
if ( !isset($info->error) ) {
869
    $data = $info;
870
}*/
871
            }
872
873
            $plugin_version = $this->_fs->get_plugin_version();
874
875
            // Get plugin's newest update.
876
            $new_version = $this->get_latest_download_details( $is_addon ? $addon->id : false, $plugin_version );
877
878
            if ( ! is_object( $new_version ) || empty( $new_version->version ) ) {
879
                $data->version = $plugin_version;
880
            } else {
881
                if ( $is_addon ) {
882
                    $data->name    = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
883
                    $data->slug    = $addon->slug;
884
                    $data->url     = WP_FS__ADDRESS;
885
                    $data->package = $new_version->url;
886
                }
887
888
                if ( ! $plugin_in_repo ) {
889
                    $data->last_updated = ! is_null( $new_version->updated ) ? $new_version->updated : $new_version->created;
890
                    $data->requires     = $new_version->requires_platform_version;
891
                    $data->tested       = $new_version->tested_up_to_version;
892
                }
893
894
                $data->version       = $new_version->version;
895
                $data->download_link = $new_version->url;
896
897
                if ( isset( $new_version->readme ) && is_object( $new_version->readme ) ) {
898
                    $new_version_readme_data = $new_version->readme;
899
                    if ( isset( $new_version_readme_data->sections ) ) {
900
                        $new_version_readme_data->sections = (array) $new_version_readme_data->sections;
901
                    } else {
902
                        $new_version_readme_data->sections = array();
903
                    }
904
905
                    if ( isset( $data->sections ) ) {
906
                        if ( isset( $data->sections['screenshots'] ) ) {
907
                            $new_version_readme_data->sections['screenshots'] = $data->sections['screenshots'];
908
                        }
909
910
                        if ( isset( $data->sections['reviews'] ) ) {
911
                            $new_version_readme_data->sections['reviews'] = $data->sections['reviews'];
912
                        }
913
                    }
914
915
                    if ( isset( $new_version_readme_data->banners ) ) {
916
                        $new_version_readme_data->banners = (array) $new_version_readme_data->banners;
917
                    } else if ( isset( $data->banners ) ) {
918
                        $new_version_readme_data->banners = $data->banners;
919
                    }
920
921
                    $wp_org_sections = array(
922
                        'author',
923
                        'author_profile',
924
                        'rating',
925
                        'ratings',
926
                        'num_ratings',
927
                        'support_threads',
928
                        'support_threads_resolved',
929
                        'active_installs',
930
                        'added',
931
                        'homepage'
932
                    );
933
934
                    foreach ( $wp_org_sections as $wp_org_section ) {
935
                        if ( isset( $data->{$wp_org_section} ) ) {
936
                            $new_version_readme_data->{$wp_org_section} = $data->{$wp_org_section};
937
                        }
938
                    }
939
940
                    $data = $new_version_readme_data;
941
                }
942
            }
943
944
            return $data;
945
        }
946
947
        /**
948
         * @author Vova Feldman (@svovaf)
949
         * @since  1.2.1.7
950
         *
951
         * @param number|bool $addon_id
952
         * @param bool|string $newer_than   Since 2.2.1
953
         * @param bool|string $fetch_readme Since 2.2.1
954
         *
955
         * @return object
956
         */
957
        private function get_latest_download_details( $addon_id = false, $newer_than = false, $fetch_readme = true ) {
958
            return $this->_fs->_fetch_latest_version( $addon_id, true, WP_FS__TIME_24_HOURS_IN_SEC, $newer_than, $fetch_readme );
959
        }
960
961
        /**
962
         * Checks if a given basename has a matching folder name
963
         * with the current context plugin.
964
         *
965
         * @author Vova Feldman (@svovaf)
966
         * @since  1.2.1.6
967
         *
968
         * @return bool
969
         */
970
        private function is_correct_folder_name() {
971
            return ( $this->_fs->get_target_folder_name() == trim( dirname( $this->_fs->get_plugin_basename() ), '/\\' ) );
972
        }
973
974
        /**
975
         * This is a special after upgrade handler for migrating modules
976
         * that didn't use the '-premium' suffix folder structure before
977
         * the migration.
978
         *
979
         * @author Vova Feldman (@svovaf)
980
         * @since  1.2.1.6
981
         *
982
         * @param bool  $response   Install response.
983
         * @param array $hook_extra Extra arguments passed to hooked filters.
984
         * @param array $result     Installation result data.
985
         *
986
         * @return bool
987
         */
988
        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...
989
            $basename = $this->_fs->get_plugin_basename();
990
991
            if ( true !== $response ||
992
                 empty( $hook_extra ) ||
993
                 empty( $hook_extra['plugin'] ) ||
994
                 $basename !== $hook_extra['plugin']
995
            ) {
996
                return $response;
997
            }
998
999
            $active_plugins_basenames = get_option( 'active_plugins' );
1000
1001
            for ( $i = 0, $len = count( $active_plugins_basenames ); $i < $len; $i ++ ) {
1002
                if ( $basename === $active_plugins_basenames[ $i ] ) {
1003
                    // Get filename including extension.
1004
                    $filename = basename( $basename );
1005
1006
                    $new_basename = plugin_basename(
1007
                        trailingslashit( $this->_fs->is_premium() ? $this->_fs->get_premium_slug() : $this->_fs->get_slug() ) .
1008
                        $filename
1009
                    );
1010
1011
                    // Verify that the expected correct path exists.
1012
                    if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $new_basename ) ) ) {
1013
                        // Override active plugin name.
1014
                        $active_plugins_basenames[ $i ] = $new_basename;
1015
                        update_option( 'active_plugins', $active_plugins_basenames );
1016
                    }
1017
1018
                    break;
1019
                }
1020
            }
1021
1022
            return $response;
1023
        }
1024
1025
        #----------------------------------------------------------------------------------
1026
        #region Auto Activation
1027
        #----------------------------------------------------------------------------------
1028
1029
        /**
1030
         * Installs and active a plugin when explicitly requested that from a 3rd party service.
1031
         *
1032
         * This logic was inspired by the TGMPA GPL licensed library by Thomas Griffin.
1033
         *
1034
         * @link   http://tgmpluginactivation.com/
1035
         *
1036
         * @author Vova Feldman
1037
         * @since  1.2.1.7
1038
         *
1039
         * @link   https://make.wordpress.org/plugins/2017/03/16/clarification-of-guideline-8-executable-code-and-installs/
1040
         *
1041
         * @uses   WP_Filesystem
1042
         * @uses   WP_Error
1043
         * @uses   WP_Upgrader
1044
         * @uses   Plugin_Upgrader
1045
         * @uses   Plugin_Installer_Skin
1046
         * @uses   Plugin_Upgrader_Skin
1047
         *
1048
         * @param number|bool $plugin_id
1049
         *
1050
         * @return array
1051
         */
1052
        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...
1053
            if ( ! empty( $plugin_id ) && ! FS_Plugin::is_valid_id( $plugin_id ) ) {
1054
                // Invalid plugin ID.
1055
                return array(
1056
                    'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
1057
                    'code'    => 'invalid_module_id',
1058
                );
1059
            }
1060
1061
            $is_addon = false;
1062
            if ( FS_Plugin::is_valid_id( $plugin_id ) &&
1063
                 $plugin_id != $this->_fs->get_id()
1064
            ) {
1065
                $addon = $this->_fs->get_addon( $plugin_id );
1066
1067
                if ( ! is_object( $addon ) ) {
1068
                    // Invalid add-on ID.
1069
                    return array(
1070
                        'message' => $this->_fs->get_text_inline( 'Invalid module ID.', 'auto-install-error-invalid-id' ),
1071
                        'code'    => 'invalid_module_id',
1072
                    );
1073
                }
1074
1075
                $slug          = $addon->slug;
1076
                $premium_slug  = $addon->premium_slug;
1077
                $title         = $addon->title . ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' );
1078
1079
                $is_addon = true;
1080
            } else {
1081
                $slug          = $this->_fs->get_slug();
1082
                $premium_slug  = $this->_fs->get_premium_slug();
1083
                $title         = $this->_fs->get_plugin_title() .
1084
                                 ( $this->_fs->is_addon() ? ' ' . $this->_fs->get_text_inline( 'Add-On', 'addon' ) : '' );
1085
            }
1086
1087
            if ( $this->is_premium_plugin_active( $plugin_id ) ) {
1088
                // Premium version already activated.
1089
                return array(
1090
                    'message' => $is_addon ?
1091
                        $this->_fs->get_text_inline( 'Premium add-on version already installed.', 'auto-install-error-premium-addon-activated' ) :
1092
                        $this->_fs->get_text_inline( 'Premium version already active.', 'auto-install-error-premium-activated' ),
1093
                    'code'    => 'premium_installed',
1094
                );
1095
            }
1096
1097
            $latest_version = $this->get_latest_download_details( $plugin_id, false, false );
1098
            $target_folder  = $premium_slug;
1099
1100
            // Prep variables for Plugin_Installer_Skin class.
1101
            $extra         = array();
1102
            $extra['slug'] = $target_folder;
1103
            $source        = $latest_version->url;
1104
            $api           = null;
1105
1106
            $install_url = add_query_arg(
1107
                array(
1108
                    'action' => 'install-plugin',
1109
                    'plugin' => urlencode( $slug ),
1110
                ),
1111
                'update.php'
1112
            );
1113
1114
            if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
1115
                // Include required resources for the installation.
1116
                require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
1117
            }
1118
1119
            $skin_args = array(
1120
                'type'   => 'web',
1121
                'title'  => sprintf( $this->_fs->get_text_inline( 'Installing plugin: %s', 'installing-plugin-x' ), $title ),
1122
                'url'    => esc_url_raw( $install_url ),
1123
                'nonce'  => 'install-plugin_' . $slug,
1124
                'plugin' => '',
1125
                'api'    => $api,
1126
                'extra'  => $extra,
1127
            );
1128
1129
//			$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...
1130
//			$skin = new Plugin_Installer_Skin( $skin_args );
1131
            $skin = new WP_Ajax_Upgrader_Skin( $skin_args );
1132
1133
            // Create a new instance of Plugin_Upgrader.
1134
            $upgrader = new Plugin_Upgrader( $skin );
1135
1136
            // Perform the action and install the plugin from the $source urldecode().
1137
            add_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1, 3 );
1138
1139
            $install_result = $upgrader->install( $source );
1140
1141
            remove_filter( 'upgrader_source_selection', array( 'FS_Plugin_Updater', '_maybe_adjust_source_dir' ), 1 );
1142
1143
            if ( is_wp_error( $install_result ) ) {
1144
                return array(
1145
                    'message' => $install_result->get_error_message(),
1146
                    'code'    => $install_result->get_error_code(),
1147
                );
1148
            } elseif ( is_wp_error( $skin->result ) ) {
1149
                return array(
1150
                    'message' => $skin->result->get_error_message(),
1151
                    'code'    => $skin->result->get_error_code(),
1152
                );
1153
            } elseif ( $skin->get_errors()->get_error_code() ) {
1154
                return array(
1155
                    'message' => $skin->get_error_messages(),
1156
                    'code'    => 'unknown',
1157
                );
1158
            } elseif ( is_null( $install_result ) ) {
1159
                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...
1160
1161
                $error_code    = 'unable_to_connect_to_filesystem';
1162
                $error_message = $this->_fs->get_text_inline( 'Unable to connect to the filesystem. Please confirm your credentials.' );
1163
1164
                // Pass through the error from WP_Filesystem if one was raised.
1165
                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...
1166
                     is_wp_error( $wp_filesystem->errors ) &&
1167
                     $wp_filesystem->errors->get_error_code()
1168
                ) {
1169
                    $error_message = $wp_filesystem->errors->get_error_message();
1170
                }
1171
1172
                return array(
1173
                    'message' => $error_message,
1174
                    'code'    => $error_code,
1175
                );
1176
            }
1177
1178
            // Grab the full path to the main plugin's file.
1179
            $plugin_activate = $upgrader->plugin_info();
1180
1181
            // Try to activate the plugin.
1182
            $activation_result = $this->try_activate_plugin( $plugin_activate );
1183
1184
            if ( is_wp_error( $activation_result ) ) {
1185
                return array(
1186
                    'message' => $activation_result->get_error_message(),
1187
                    'code'    => $activation_result->get_error_code(),
1188
                );
1189
            }
1190
1191
            return $skin->get_upgrade_messages();
1192
        }
1193
1194
        /**
1195
         * Tries to activate a plugin. If fails, returns the error.
1196
         *
1197
         * @author Vova Feldman
1198
         * @since  1.2.1.7
1199
         *
1200
         * @param string $file_path Path within wp-plugins/ to main plugin file.
1201
         *                          This determines the styling of the output messages.
1202
         *
1203
         * @return bool|WP_Error
1204
         */
1205
        protected function try_activate_plugin( $file_path ) {
1206
            $activate = activate_plugin( $file_path, '', $this->_fs->is_network_active() );
1207
1208
            return is_wp_error( $activate ) ?
1209
                $activate :
1210
                true;
1211
        }
1212
1213
        /**
1214
         * Check if a premium module version is already active.
1215
         *
1216
         * @author Vova Feldman
1217
         * @since  1.2.1.7
1218
         *
1219
         * @param number|bool $plugin_id
1220
         *
1221
         * @return bool
1222
         */
1223
        private function is_premium_plugin_active( $plugin_id = false ) {
1224
            if ( $plugin_id != $this->_fs->get_id() ) {
1225
                return $this->_fs->is_addon_activated( $plugin_id, true );
1226
            }
1227
1228
            return is_plugin_active( $this->_fs->premium_plugin_basename() );
1229
        }
1230
1231
        /**
1232
         * Store the basename since it's not always available in the `_maybe_adjust_source_dir` method below.
1233
         *
1234
         * @author Leo Fajardo (@leorw)
1235
         * @since 2.2.1
1236
         *
1237
         * @param bool|WP_Error $response   Response.
1238
         * @param array         $hook_extra Extra arguments passed to hooked filters.
1239
         *
1240
         * @return bool|WP_Error
1241
         */
1242
        static function _store_basename_for_source_adjustment( $response, $hook_extra ) {
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...
1243
            if ( isset( $hook_extra['plugin'] ) ) {
1244
                self::$_upgrade_basename = $hook_extra['plugin'];
1245
            } else if ( $hook_extra['theme'] ) {
1246
                self::$_upgrade_basename = $hook_extra['theme'];
1247
            } else {
1248
                self::$_upgrade_basename = null;
1249
            }
1250
1251
            return $response;
1252
        }
1253
1254
        /**
1255
         * Adjust the plugin directory name if necessary.
1256
         * Assumes plugin has a folder (not a single file plugin).
1257
         *
1258
         * The final destination directory of a plugin is based on the subdirectory name found in the
1259
         * (un)zipped source. In some cases this subdirectory name is not the same as the expected
1260
         * slug and the plugin will not be recognized as installed. This is fixed by adjusting
1261
         * the temporary unzipped source subdirectory name to the expected plugin slug.
1262
         *
1263
         * @author Vova Feldman
1264
         * @since  1.2.1.7
1265
         * @since  2.2.1 The method was converted to static since when the admin update bulk products via the Updates section, the logic applies the `upgrader_source_selection` filter for every product that is being updated.
1266
         *
1267
         * @param string       $source        Path to upgrade/zip-file-name.tmp/subdirectory/.
1268
         * @param string       $remote_source Path to upgrade/zip-file-name.tmp.
1269
         * @param \WP_Upgrader $upgrader      Instance of the upgrader which installs the plugin.
1270
         *
1271
         * @return string|WP_Error
1272
         */
1273
        static 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...
1274
            if ( ! is_object( $GLOBALS['wp_filesystem'] ) ) {
1275
                return $source;
1276
            }
1277
1278
            $basename = self::$_upgrade_basename;
1279
            $is_theme = false;
1280
1281
            // Figure out what the slug is supposed to be.
1282
            if ( isset( $upgrader->skin->options['extra'] ) ) {
1283
                // Set by the auto-install logic.
1284
                $desired_slug = $upgrader->skin->options['extra']['slug'];
1285
            } else if ( ! empty( $basename ) ) {
1286
                /**
1287
                 * If it doesn't end with ".php", it's a theme.
1288
                 *
1289
                 * @author Leo Fajardo (@leorw)
1290
                 * @since 2.2.1
1291
                 */
1292
                $is_theme = ( ! fs_ends_with( $basename, '.php' ) );
1293
1294
                $desired_slug = ( ! $is_theme ) ?
1295
                    dirname( $basename ) :
1296
                    // Theme slug
1297
                    $basename;
1298
            } else {
1299
                // Can't figure out the desired slug, stop the execution.
1300
                return $source;
1301
            }
1302
1303
            if ( is_multisite() ) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
1304
                /**
1305
                 * If we are running in a multisite environment and the product is not network activated,
1306
                 * the instance will not exist anyway. Therefore, try to update the source if necessary
1307
                 * regardless if the Freemius instance of the product exists or not.
1308
                 *
1309
                 * @author Vova Feldman
1310
                 */
1311
            } else if ( ! empty( $basename ) ) {
1312
                $fs = Freemius::get_instance_by_file(
1313
                    $basename,
1314
                    $is_theme ?
1315
                        WP_FS__MODULE_TYPE_THEME :
1316
                        WP_FS__MODULE_TYPE_PLUGIN
1317
                );
1318
1319
                if ( ! is_object( $fs ) ) {
1320
                    /**
1321
                     * If the Freemius instance does not exist on a non-multisite network environment, it means that:
1322
                     *  1. The product is not powered by Freemius; OR
1323
                     *  2. The product is not activated, therefore, we don't mind if after the update the folder name will change.
1324
                     *
1325
                     * @author Leo Fajardo (@leorw)
1326
                     * @since  2.2.1
1327
                     */
1328
                    return $source;
1329
                }
1330
            }
1331
1332
            $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
1333
1334
            if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
1335
                $from_path = untrailingslashit( $source );
1336
                $to_path   = trailingslashit( $remote_source ) . $desired_slug;
1337
1338
                if ( true === $GLOBALS['wp_filesystem']->move( $from_path, $to_path ) ) {
1339
                    return trailingslashit( $to_path );
1340
                }
1341
1342
                return new WP_Error(
1343
                    'rename_failed',
1344
                    fs_text_inline( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'module-package-rename-failure' ),
1345
                    array(
1346
                        'found'    => $subdir_name,
1347
                        'expected' => $desired_slug
1348
                    )
1349
                );
1350
            }
1351
1352
            return $source;
1353
        }
1354
1355
        #endregion
1356
    }