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 ( 8970a4...b3ee5f )
by Brad
06:24 queued 03:11
created

FS_Plugin_Updater   F

Complexity

Total Complexity 137

Size/Duplication

Total Lines 1098
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 3

Importance

Changes 0
Metric Value
dl 0
loc 1098
rs 1.208
c 0
b 0
f 0
wmc 137
lcom 2
cbo 3

24 Methods

Rating   Name   Duplication   Size   Complexity  
A instance() 0 9 2
A __construct() 0 7 1
B filters() 0 43 6
A add_transient_filters() 0 11 1
A remove_transient_filters() 0 11 1
A catch_plugin_update_row() 0 3 1
A edit_and_echo_plugin_update_row() 0 24 2
A change_theme_update_info_html() 0 29 4
A http_request_host_is_external_filter() 0 3 2
F pre_set_site_transient_update_plugins_filter() 0 100 22
A get_update_details() 0 23 2
C set_update_data() 0 59 12
A delete_update_data() 0 27 5
A _fetch_plugin_info_from_repository() 0 28 5
B fetch_wp_org_module_translation_updates() 0 62 7
C get_installed_translations() 0 48 12
C plugins_api_filter() 0 74 13
A get_latest_download_details() 0 3 1
A is_correct_folder_name() 0 7 2
B _maybe_update_folder_name() 0 36 9
F install_and_activate_plugin() 0 139 18
A try_activate_plugin() 0 7 2
A is_premium_plugin_active() 0 7 2
A _maybe_adjust_source_dir() 0 29 5

How to fix   Complexity   

Complex Class

Complex classes like FS_Plugin_Updater often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FS_Plugin_Updater, and based on these observations, apply Extract Interface, too.

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