Completed
Push — update/log-api-endpoint ( 49f963...4e96f5 )
by
unknown
49:55
created

Jetpack_Autoupdate   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 239
Duplicated Lines 13.39 %

Coupling/Cohesion

Components 1
Dependencies 3
Metric Value
wmc 45
lcom 1
cbo 3
dl 32
loc 239
rs 8.3673

12 Methods

Rating   Name   Duplication   Size   Complexity  
A init() 0 6 2
A __construct() 0 8 2
A autoupdate_plugin() 8 8 2
A autoupdate_theme() 8 8 2
A autoupdate_core() 0 7 2
A expect() 0 6 2
A automatic_updates_complete() 0 17 4
A get_log() 0 7 1
B log_items() 0 18 5
B bump_stats() 16 36 6
B get_successful_updates() 0 21 6
F get_possible_failures() 0 43 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Autoupdate 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 Jetpack_Autoupdate, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * Handles items that have been selected for automatic updates.
5
 * Hooks into WP_Automatic_Updater
6
 */
7
class Jetpack_Autoupdate {
8
9
	private $results = array();
10
11
	private $expected = array();
12
13
	private $success = array(
14
		'plugin' => array(),
15
		'theme' => array(),
16
	);
17
18
	private $failed = array(
19
		'plugin' => array(),
20
		'theme' => array(),
21
	);
22
23
	private static $instance = null;
24
25
	static function init() {
26
		if ( is_null( self::$instance ) ) {
1 ignored issue
show
Coding Style introduced by
As per coding-style, please use === null instead of is_null.
Loading history...
27
			self::$instance = new Jetpack_Autoupdate;
28
		}
29
		return self::$instance;
30
	}
31
32
	private function __construct() {
33
		if ( Jetpack::is_module_active( 'manage' ) ) {
34
			add_filter( 'auto_update_plugin',  array( $this, 'autoupdate_plugin' ), 10, 2 );
35
			add_filter( 'auto_update_theme',   array( $this, 'autoupdate_theme' ), 10, 2 );
36
			add_filter( 'auto_update_core',    array( $this, 'autoupdate_core' ), 10, 2 );
37
			add_action( 'automatic_updates_complete', array( $this, 'automatic_updates_complete' ), 999, 1 );
38
		}
39
	}
40
41 View Code Duplication
	public function autoupdate_plugin( $update, $item ) {
42
		$autoupdate_plugin_list = Jetpack_Options::get_option( 'autoupdate_plugins', array() );
43
		if ( in_array( $item->plugin, $autoupdate_plugin_list ) ) {
44
			$this->expect( $item->plugin, 'plugin' );
45
 			return true;
46
		}
47
		return $update;
48
	}
49
50 View Code Duplication
	public function autoupdate_theme( $update, $item ) {
51
		$autoupdate_theme_list = Jetpack_Options::get_option( 'autoupdate_themes', array() );
52
		if ( in_array( $item->theme , $autoupdate_theme_list) ) {
53
			$this->expect( $item->theme, 'theme' );
54
			return true;
55
		}
56
		return $update;
57
	}
58
59
	public function autoupdate_core( $update, $item ) {
0 ignored issues
show
Unused Code introduced by
The parameter $item 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...
60
		$autoupdate_core = Jetpack_Options::get_option( 'autoupdate_core', false );
61
		if ( $autoupdate_core ) {
62
			return $autoupdate_core;
63
		}
64
		return $update;
65
	}
66
67
	/**
68
	 * Stores the an item identifier to the expected array.
69
	 *
70
	 * @param string $item  Example: 'jetpack/jetpack.php' for type 'plugin' or 'twentyfifteen' for type 'theme'
71
	 * @param string $type 'plugin' or 'theme'
72
	 */
73
	private function expect( $item, $type ) {
74
		if ( ! isset( $this->expected[ $type ] ) ) {
75
			$this->expected[ $type ] = array();
76
		}
77
		$this->expected[ $type ][] = $item;
78
	}
79
80
	/**
81
	 * On completion of an automatic update, let's store the results.
82
	 *
83
	 * @param $results - Sent by WP_Automatic_Updater after it completes an autoupdate action. Results may be empty.
84
	 */
85
	public function automatic_updates_complete( $results ) {
86
		if ( empty( $this->expected ) ) {
87
			return;
88
		}
89
		$this->results = empty( $results ) ? self::get_possible_failures() : $results;
90
91
		add_action( 'shutdown', array( $this, 'bump_stats' ) );
92
93
		Jetpack::init();
94
95
		$items_to_log = array( 'plugin', 'theme' );
96
		foreach( $items_to_log as $items ) {
97
			$this->log_items( $items );
98
		}
99
100
		Jetpack::log( 'autoupdates', $this->get_log() );
101
	}
102
103
	public function get_log() {
104
		return array(
105
			'results'	=> $this->results,
106
			'failed'	=> $this->failed,
107
			'success'	=> $this->success
108
		);
109
	}
110
111
	/**
112
	 * Iterates through expected items ( plugins or themes ) and compares them to actual results.
113
	 *
114
	 * @param $items 'plugin' or 'theme'
115
	 */
116
	private function log_items( $items ) {
117
118
		if ( ! isset( $this->expected[ $items ] ) ) {
119
			return;
120
		}
121
122
		$item_results = $this->get_successful_updates( $items );
123
124
		if ( is_array( $this->expected[ $items ] ) ) {
125
			foreach( $this->expected[ $items ] as $item ) {
126
				if ( in_array( $item, $item_results ) ) {
127
						$this->success[ $items ][] = $item;
128
				} else {
129
						$this->failed[ $items ][] = $item;
130
				}
131
			}
132
		}
133
	}
134
135
	public function bump_stats() {
136
		$instance = Jetpack::init();
137
		$log = array();
138
		// Bump numbers
139 View Code Duplication
		if ( ! empty( $this->success['plugin'] ) ) {
140
			$instance->stat( 'autoupdates/plugin-success', count( $this->success['plugin'] ) );
141
			$log['plugins_success'] = $this->success['plugin'];
142
		}
143
144 View Code Duplication
		if ( ! empty( $this->failed['plugin'] ) ) {
145
			$instance->stat( 'autoupdates/plugin-fail', count( $this->failed['plugin'] ) );
146
			$log['plugins_failed'] = $this->failed['plugin'];
147
		}
148
149 View Code Duplication
		if ( ! empty( $this->success['theme'] ) ) {
150
			$instance->stat( 'autoupdates/theme-success', count( $this->success['theme'] ) );
151
			$log['themes_success'] = $this->success['theme'];
152
		}
153
154 View Code Duplication
		if ( ! empty( $this->failed['theme'] ) ) {
155
			$instance->stat( 'autoupdates/theme-fail', count( $this->failed['theme'] ) );
156
			$log['themes_failed'] = $this->failed['theme'];
157
		}
158
159
		$instance->do_stats( 'server_side' );
160
161
		// Send a more detailed log to logstash
162
		if ( ! empty( $log ) ) {
163
			Jetpack::load_xml_rpc_client();
164
			$xml = new Jetpack_IXR_Client( array(
165
				'user_id' => get_current_user_id()
166
			) );
167
			$log['blog_id'] = Jetpack_Options::get_option( 'id' );
168
			$xml->query( 'jetpack.debug_autoupdate', $log );
169
		}
170
	}
171
172
	/**
173
	 * Parses the autoupdate results generated by WP_Automatic_Updater and returns a simple array of successful items
174
	 *
175
	 * @param string $type 'plugin' or 'theme'
176
	 *
177
	 * @return array
178
	 */
179
	private function get_successful_updates( $type ) {
180
		$successful_updates = array();
181
182
		if ( ! isset( $this->results[ $type ] ) ) {
183
			return $successful_updates;
184
		}
185
186
		foreach( $this->results[ $type ] as $result ) {
187
			if ( $result->result ) {
188
				switch( $type ) {
189
					case 'theme':
190
						$successful_updates[] = $result->item->theme;
191
						break;
192
					case 'plugin':
193
						$successful_updates[] = $result->item->plugin;
194
				}
195
			}
196
		}
197
198
		return $successful_updates;
199
	}
200
201
	static function get_possible_failures() {
202
		$result = array();
203
		// Lets check some reasons why it might not be working as expected
204
		include_once( ABSPATH . '/wp-admin/includes/admin.php' );
205
		include_once( ABSPATH . '/wp-admin/includes/class-wp-upgrader.php' );
206
		$upgrader = new WP_Automatic_Updater;
207
208
		if ( $upgrader->is_disabled() ) {
209
			$result[] = 'autoupdates-disabled';
210
		}
211
		if ( ! is_main_site() ) {
212
			$result[] = 'is-not-main-site';
213
		}
214
		if ( ! is_main_network() ) {
215
			$result[] = 'is-not-main-network';
216
		}
217
		if ( $upgrader->is_vcs_checkout( ABSPATH ) ) {
218
			$result[] = 'site-on-vcs';
219
		}
220
		if ( $upgrader->is_vcs_checkout( WP_PLUGIN_DIR ) ) {
221
			$result[] = 'plugin-directory-on-vcs';
222
		}
223
		if ( $upgrader->is_vcs_checkout( WP_CONTENT_DIR ) ) {
224
			$result[] = 'content-directory-on-vcs';
225
		}
226
		$lock = get_option( 'auto_updater.lock' );
227
		if ( $lock > ( time() - HOUR_IN_SECONDS ) ) {
228
			$result[] = 'lock-is-set';
229
		}
230
		$skin = new Automatic_Upgrader_Skin;
231
		include_once( ABSPATH . 'wp-admin/includes/file.php' );
232
		include_once( ABSPATH . 'wp-admin/includes/template.php' );
233
		if ( ! $skin->request_filesystem_credentials( false, ABSPATH, false ) ) {
234
			$result[] = 'no-system-write-access';
235
		}
236
		if ( ! $skin->request_filesystem_credentials( false, WP_PLUGIN_DIR, false )  ) {
237
			$result[] = 'no-plugin-directory-write-access';
238
		}
239
		if ( ! $skin->request_filesystem_credentials( false,  WP_CONTENT_DIR, false ) ) {
240
			$result[] = 'no-wp-content-directory-write-access';
241
		}
242
		return $result;
243
	}
244
245
}
1 ignored issue
show
Coding Style introduced by
According to PSR2, the closing brace of classes should be placed on the next line directly after the body.

Below you find some examples:

// Incorrect placement according to PSR2
class MyClass
{
    public function foo()
    {

    }
    // This blank line is not allowed.

}

// Correct
class MyClass
{
    public function foo()
    {

    } // No blank lines after this line.
}
Loading history...
246
Jetpack_Autoupdate::init();
247