Completed
Push — update/plugins-install-endpoin... ( 492190...6f8147 )
by
unknown
13:18
created

install()   C

Complexity

Conditions 11
Paths 22

Size

Total Lines 49
Code Lines 27

Duplication

Lines 3
Ratio 6.12 %
Metric Value
dl 3
loc 49
rs 5.2653
cc 11
eloc 27
nc 22
nop 0

How to fix   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
2
3
include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
4
include_once ABSPATH . 'wp-admin/includes/file.php';
5
6
class Jetpack_JSON_API_Plugins_Install_Endpoint extends Jetpack_JSON_API_Plugins_Endpoint {
7
8
	// POST /sites/%s/plugins/%s/install
9
	protected $needed_capabilities = 'install_plugins';
10
	protected $action              = 'install';
11
12
	protected function install() {
13
		foreach ( $this->plugins as $index => $slug ) {
14
15
			$skin      = new Jetpack_Automatic_Plugin_Install_Skin();
16
			$upgrader  = new Plugin_Upgrader( $skin );
17
			$zip_url   = self::generate_wordpress_org_plugin_download_link( $slug );
18
19
			$result = $upgrader->install( $zip_url );
20
21
			if ( ! $this->bulk && is_wp_error( $result ) ) {
22
				return $result;
23
			}
24
25
			$plugin = self::get_plugin_id_by_slug( $slug );
26
			$error_code = 'install_error';
27 View Code Duplication
			if ( ! $plugin ) {
28
				$error = $this->log[ $slug ]['error'] = __( 'There was an error installing your plugin', 'jetpack' );
29
			}
30
31
			if ( ! $this->bulk && ! $result ) {
32
				$error_code = $upgrader->skin->get_main_error_code();
33
				$message = $upgrader->skin->get_main_error_message();
34
				$error = $this->log[ $slug ]['error'] = $message ? $message : __( 'An unknown error occurred during installation' , 'jetpack' );
35
			}
36
37
			$this->log[ $plugin ][] = $upgrader->skin->get_upgrade_messages();
38
		}
39
40
		if ( ! $this->bulk && isset( $error ) ) {
41
42
			if ( $error_code === 'download_failed' ) {
0 ignored issues
show
Bug introduced by
The variable $error_code does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
43
				// For backwards compatibility: versions prior to 3.9 would return no_package instead of download_failed.
44
				$error_code = 'no_package';
45
			}
46
47
			error_log( print_r( array(
48
				'code' => $error_code,
49
				'message' => $error,
50
				'log' => $upgrader->skin->get_upgrade_messages()
0 ignored issues
show
Bug introduced by
The variable $upgrader does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
51
			) ) );
52
53
			return new WP_Error( $error_code, $this->log[ $slug ]['error'], 400 );
0 ignored issues
show
Bug introduced by
The variable $slug seems to be defined by a foreach iteration on line 13. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
54
		}
55
56
		// replace the slug with the actual plugin id
57
		$this->plugins[ $index ] = $plugin;
0 ignored issues
show
Bug introduced by
The variable $index seems to be defined by a foreach iteration on line 13. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
Bug introduced by
The variable $plugin does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
58
59
		return true;
60
	}
61
62
	protected function validate_plugins() {
63 View Code Duplication
		if ( empty( $this->plugins ) || ! is_array( $this->plugins ) ) {
64
			return new WP_Error( 'missing_plugins', __( 'No plugins found.', 'jetpack' ) );
65
		}
66
		foreach( $this->plugins as $index => $slug ) {
67
68
			// make sure it is not already installed
69
			if ( self::get_plugin_id_by_slug( $slug ) ) {
70
				return new WP_Error( 'plugin_already_installed', __( 'The plugin is already installed', 'jetpack' ) );
71
			}
72
73
		}
74
		return true;
75
	}
76
77
	protected static function generate_wordpress_org_plugin_download_link( $plugin_slug ) {
78
		return "https://downloads.wordpress.org/plugin/{$plugin_slug}.latest-stable.zip";
79
	}
80
81
	protected static function get_plugin_id_by_slug( $slug ) {
82
		$plugins = get_plugins();
83
		if ( ! is_array( $plugins ) ) {
84
			return false;
85
		}
86
		foreach( $plugins as $id => $plugin_data ) {
87
			if ( strpos( $id, $slug ) !== false ) {
88
				return $id;
89
			}
90
		}
91
		return false;
92
	}
93
}
94
/**
95
 * Allows us to capture that the site doesn't have proper file system access.
96
 * In order to update the plugin.
97
 */
98
class Jetpack_Automatic_Plugin_Install_Skin extends Automatic_Upgrader_Skin {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
99
	/**
100
	 * Stores the last error key;
101
	 **/
102
	protected $main_error_code = 'install_error';
103
104
	/**
105
	 * Stores the last error message.
106
	 **/
107
	protected $main_error_message = 'An unknown error occurred during installation';
108
109
	/**
110
	 * Overwrites the set_upgrader to be able to tell if we e ven have the ability to write to the files.
111
	 *
112
	 * @param WP_Upgrader $upgrader
113
	 *
114
	 */
115
	public function set_upgrader( &$upgrader ) {
116
		parent::set_upgrader( $upgrader );
117
118
		// Check if we even have permission to.
119
		$result = $upgrader->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
120
		if ( ! $result ) {
121
			// set the string here since they are not available just yet
122
			$upgrader->generic_strings();
123
			$this->feedback( 'fs_unavailable' );
124
		}
125
	}
126
127
	/**
128
	 * Overwrites the error function
129
	 */
130
	public function error( $error ) {
131
		if ( is_wp_error( $error ) ) {
132
			$this->feedback( $error );
133
		}
134
	}
135
136
	private function set_main_error_code( $code ) {
137
		// Don't set the process_failed as code since it is not that helpful unless we don't have one already set.
138
		$this->main_error_code = ( $code === 'process_failed' && $this->main_error_code  ? $this->main_error_code : $code );
139
	}
140
141
	private function set_main_error_message( $message, $code ) {
142
		// Don't set the process_failed as message since it is not that helpful unless we don't have one already set.
143
		$this->main_error_message = ( $code === 'process_failed' && $this->main_error_code ? $this->main_error_code : $message );
144
	}
145
146
	public function get_main_error_code() {
147
		return $this->main_error_code;
148
	}
149
150
	public function get_main_error_message() {
151
		return $this->main_error_message;
152
	}
153
154
	/**
155
	 * Overwrites the feedback function
156
	 */
157
	public function feedback( $data ) {
158
159
		$current_error = null;
160
		if ( is_wp_error( $data ) ) {
161
			$this->set_main_error_code( $data->get_error_code() );
162
			$string = $data->get_error_message();
163
		} elseif ( is_array( $data ) ) {
164
			return;
165
		} else {
166
			$string = $data;
167
		}
168
169
		if ( ! empty( $this->upgrader->strings[ $string ] ) ) {
170
			$this->set_main_error_code( $string );
171
172
			$current_error = $string;
173
			$string = $this->upgrader->strings[ $string ];
174
		}
175
176
		if ( strpos( $string, '%' ) !== false ) {
177
			$args = func_get_args();
178
			$args = array_splice( $args, 1 );
179
			if ( ! empty( $args ) )
180
				$string = vsprintf( $string, $args );
181
		}
182
183
		$string = trim( $string );
184
		$string = wp_kses( $string, array(
185
			'a' => array(
186
				'href' => true
187
			),
188
			'br' => true,
189
			'em' => true,
190
			'strong' => true,
191
		) );
192
193
		$this->set_main_error_message( $string, $current_error );
194
		$this->messages[] = $string;
195
	}
196
}
197