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

Jetpack_Autoupdate::expect()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 6
rs 9.4286
nc 2
cc 2
eloc 4
nop 2
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