GateKeeper   D
last analyzed

Complexity

Total Complexity 58

Size/Duplication

Total Lines 328
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 4
Bugs 1 Features 0
Metric Value
eloc 122
c 4
b 1
f 0
dl 0
loc 328
ccs 0
cts 145
cp 0
rs 4.5599
wmc 58

24 Methods

Rating   Name   Duplication   Size   Complexity  
A isPluginActive() 0 4 2
A printNotices() 0 4 2
A ajaxActivatePluginLink() 0 16 2
A getDependencyLinks() 0 4 1
A getAllPlugins() 0 4 1
A hasPendingDependencies() 0 8 3
A getPluginSlug() 0 3 1
A init() 0 8 1
A activatePlugin() 0 13 4
A setDependencyNotice() 0 11 4
A catchError() 0 11 4
A hasDependency() 0 6 3
A getMustUsePlugins() 0 10 3
A isPluginInstalled() 0 4 1
A getPluginData() 0 15 4
A getPluginRequirements() 0 7 2
A isPluginVersionValid() 0 12 3
B getDependencyActions() 0 15 8
A isPluginValid() 0 3 2
A isPluginDependency() 0 3 1
A getPlugin() 0 6 2
A getPluginLink() 0 12 2
A getPluginInformation() 0 3 1
A __construct() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like GateKeeper 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.

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 GateKeeper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace GeminiLabs\Pollux;
4
5
use Exception;
6
use ReflectionClass;
7
8
class GateKeeper
9
{
10
	public $errors = [];
11
12
	/**
13
	 * [plugin_file_path] => [plugin_name]|[plugin_version]|[plugin_url]
14
	 */
15
	protected $dependencies = [];
16
17
	/**
18
	 * @var Notice
19
	 */
20
	protected $notice;
21
22
	/**
23
	 * @var string
24
	 */
25
	protected $plugin;
26
27
	public function __construct( $plugin, $dependencies = [] )
28
	{
29
		$this->dependencies = $dependencies;
30
		$this->plugin = $plugin;
31
		add_action( 'admin_init', [$this, 'init'] );
32
	}
33
34
	public function init()
35
	{
36
		$this->notice = pollux_app()->make( 'Notice' );
37
38
		add_action( 'current_screen',                         [$this, 'activatePlugin'] );
39
		add_action( 'wp_ajax_pollux/dependency/activate_url', [$this, 'ajaxActivatePluginLink'] );
40
		add_action( 'admin_notices',                          [$this, 'printNotices'] );
41
		add_action( 'current_screen',                         [$this, 'setDependencyNotice'] );
42
	}
43
44
	/**
45
	 * @return void
46
	 */
47
	public function activatePlugin()
48
	{
49
		if( get_current_screen()->id != sprintf( 'settings_page_%s', pollux_app()->id )
0 ignored issues
show
Bug introduced by
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
50
			|| filter_input( INPUT_GET, 'action' ) != 'activate'
51
		)return;
52
		$plugin = filter_input( INPUT_GET, 'plugin' );
53
		check_admin_referer( 'activate-plugin_' . $plugin );
54
		$result = activate_plugin( $plugin, null, is_network_admin(), true );
55
		if( is_wp_error( $result )) {
56
			wp_die( $result->get_error_message() );
57
		}
58
		wp_safe_redirect( wp_get_referer() );
59
		exit;
60
	}
61
62
	/**
63
	 * @return void
64
	 */
65
	public function ajaxActivatePluginLink()
66
	{
67
		check_ajax_referer( 'updates' );
68
		$plugin = filter_input( INPUT_POST, 'plugin' );
69
		if( !$this->isPluginDependency( $plugin )) {
70
			wp_send_json_error();
71
		}
72
		$activateUrl = add_query_arg([
73
			'_wpnonce' => wp_create_nonce( sprintf( 'activate-plugin_%s', $plugin )),
74
			'action' => 'activate',
75
			'page' => pollux_app()->id,
76
			'plugin' => $plugin,
77
		], self_admin_url( 'options-general.php' ));
78
		wp_send_json_success([
79
			'activate_url' => $activateUrl,
80
			filter_input( INPUT_POST, 'type' ) => $plugin,
81
		]);
82
	}
83
84
	/**
85
	 * @return bool
86
	 */
87
	public function hasDependency( $plugin )
88
	{
89
		if( !$this->isPluginDependency( $plugin )) {
90
			return true;
91
		}
92
		return $this->isPluginInstalled( $plugin ) && $this->isPluginValid( $plugin );
93
	}
94
95
	/**
96
	 * @return bool
97
	 */
98
	public function hasPendingDependencies()
99
	{
100
		foreach( $this->dependencies as $plugin => $data ) {
101
			if( !$this->isPluginDependency( $plugin ))continue;
102
			$this->isPluginActive( $plugin );
103
			$this->isPluginVersionValid( $plugin );
104
		}
105
		return !empty( $this->errors );
106
	}
107
108
	/**
109
	 * @return bool
110
	 */
111
	public function isPluginActive( $plugin )
112
	{
113
		return $this->catchError( $plugin, 'inactive',
114
			is_plugin_active( $plugin ) || array_key_exists( $plugin, $this->getMustUsePlugins() )
115
		);
116
	}
117
118
	/**
119
	 * @return bool
120
	 */
121
	public function isPluginDependency( $plugin )
122
	{
123
		return array_key_exists( $plugin, $this->dependencies );
124
	}
125
126
	/**
127
	 * @return bool
128
	 */
129
	public function isPluginInstalled( $plugin )
130
	{
131
		return $this->catchError( $plugin, 'not_found',
132
			array_key_exists( $plugin, $this->getAllPlugins() )
133
		);
134
	}
135
136
	/**
137
	 * @return bool
138
	 */
139
	public function isPluginValid( $plugin )
140
	{
141
		return $this->isPluginActive( $plugin ) && $this->isPluginVersionValid( $plugin );
142
	}
143
144
	/**
145
	 * @return bool
146
	 */
147
	public function isPluginVersionValid( $plugin )
148
	{
149
		if( !$this->isPluginDependency( $plugin )) {
150
			return true;
151
		}
152
		if( !$this->isPluginInstalled( $plugin )) {
153
			return false;
154
		}
155
		return $this->catchError( $plugin, 'wrong_version', version_compare(
156
			$this->getPluginRequirements( $plugin, 'version' ),
0 ignored issues
show
Bug introduced by
It seems like $this->getPluginRequirements($plugin, 'version') can also be of type array; however, parameter $version1 of version_compare() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
			/** @scrutinizer ignore-type */ $this->getPluginRequirements( $plugin, 'version' ),
Loading history...
157
			$this->getAllPlugins()[$plugin]['Version'],
158
			'<='
159
		));
160
	}
161
162
	/**
163
	 * @return void
164
	 */
165
	public function printNotices()
166
	{
167
		foreach( $this->notice->all as $notice ) {
168
			echo $this->notice->generate( $notice );
169
		}
170
	}
171
172
	/**
173
	 * @return void|null
174
	 */
175
	public function setDependencyNotice()
176
	{
177
		if( get_current_screen()->id != 'settings_page_pollux'
0 ignored issues
show
Bug introduced by
Are you sure the usage of get_current_screen() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
178
			|| pollux_app()->config->disable_config
179
			|| !$this->hasPendingDependencies()
180
		)return;
181
		$message = sprintf( '<strong>%s:</strong> %s',
182
			__( 'Pollux requires the latest version of the following plugins', 'pollux' ),
183
			$this->getDependencyLinks()
184
		);
185
		$this->notice->addWarning( [$message, $this->getDependencyActions()] );
186
	}
187
188
	/**
189
	 * @param string $plugin
190
	 * @param string $error
191
	 * @param bool $isValid
192
	 * @return bool
193
	 */
194
	protected function catchError( $plugin, $error, $isValid )
195
	{
196
		if( !$isValid && $this->isPluginDependency( $plugin )) {
197
			if( !isset( $this->errors[$plugin] )) {
198
				$this->errors[$plugin] = [];
199
			}
200
			$this->errors[$plugin] = array_keys( array_flip(
201
				array_merge( $this->errors[$plugin], [$error] )
202
			));
203
		}
204
		return $isValid;
205
	}
206
207
	/**
208
	 * @return array
209
	 */
210
	protected function getAllPlugins()
211
	{
212
		require_once ABSPATH . 'wp-admin/includes/plugin.php';
213
		return array_merge( get_plugins(), $this->getMustUsePlugins() );
214
	}
215
216
	/**
217
	 * @return string
218
	 */
219
	protected function getDependencyActions()
220
	{
221
		$actions = '';
222
		foreach( $this->errors as $plugin => $errors ) {
223
			if( in_array( 'not_found', $errors ) && current_user_can( 'install_plugins' )) {
224
				$actions .= $this->notice->installButton( $this->getPluginRequirements( $plugin ));
225
			}
226
			else if( in_array( 'wrong_version', $errors ) && current_user_can( 'update_plugins' )) {
227
				$actions .= $this->notice->updateButton( $this->getPluginInformation( $plugin ));
228
			}
229
			else if( in_array( 'inactive', $errors ) && current_user_can( 'activate_plugins' )) {
230
				$actions .= $this->notice->activateButton( $this->getPluginInformation( $plugin ));
231
			}
232
		}
233
		return $actions;
234
	}
235
236
	/**
237
	 * @return string
238
	 */
239
	protected function getDependencyLinks()
240
	{
241
		return array_reduce( array_keys( $this->errors ), function( $carry, $plugin ) {
242
			return $carry . $this->getPluginLink( $plugin );
243
		});
244
	}
245
246
	/**
247
	 * @return array
248
	 */
249
	protected function getMustUsePlugins()
250
	{
251
		$plugins = get_mu_plugins();
252
		if( in_array( 'Bedrock Autoloader', array_column( $plugins, 'Name' ))) {
253
			$autoloadedPlugins = get_site_option( 'bedrock_autoloader' );
254
			if( !empty( $autoloadedPlugins['plugins'] )) {
255
				return array_merge( $plugins, $autoloadedPlugins['plugins'] );
256
			}
257
		}
258
		return $plugins;
259
	}
260
261
	/**
262
	 * @return array|false
263
	 */
264
	protected function getPlugin( $plugin )
265
	{
266
		if( $this->isPluginInstalled( $plugin )) {
267
			return $this->getAllPlugins()[$plugin];
268
		}
269
		return false;
270
	}
271
272
	/**
273
	 * @return array|string
274
	 */
275
	protected function getPluginData( $plugin, $data, $key = null )
276
	{
277
		if( !is_array( $data )) {
278
			throw new Exception( sprintf( 'Plugin information not found for: %s', $plugin ));
279
		}
280
		$data['plugin'] = $plugin;
281
		$data['slug'] = $this->getPluginSlug( $plugin );
282
		$data = array_change_key_case( $data );
283
		if( is_null( $key )) {
284
			return $data;
285
		}
286
		$key = strtolower( $key );
287
		return isset( $data[$key] )
288
			? $data[$key]
289
			: '';
290
	}
291
292
	/**
293
	 * @return array|string
294
	 */
295
	protected function getPluginInformation( $plugin, $key = null )
296
	{
297
		return $this->getPluginData( $plugin, $this->getPlugin( $plugin ), $key );
298
	}
299
300
	/**
301
	 * @return string
302
	 */
303
	protected function getPluginLink( $plugin )
304
	{
305
		try {
306
			$data = $this->getPluginInformation( $plugin );
307
		}
308
		catch( Exception $e ) {
309
			$data = $this->getPluginRequirements( $plugin );
310
		}
311
		return sprintf( '<span class="plugin-%s"><a href="%s">%s</a></span>',
312
			$data['slug'],
313
			$data['pluginuri'],
314
			$data['name']
315
		);
316
	}
317
318
	/**
319
	 * @return array|string
320
	 */
321
	protected function getPluginRequirements( $plugin, $key = null )
322
	{
323
		$keys = ['Name', 'Version', 'PluginURI'];
324
		$requirements = $this->isPluginDependency( $plugin )
325
			? array_pad( explode( '|', $this->dependencies[$plugin] ), 3, '' )
326
			: array_fill( 0, 3, '' );
327
		return $this->getPluginData( $plugin, array_combine( $keys, $requirements ), $key );
328
	}
329
330
	/**
331
	 * @return string
332
	 */
333
	protected function getPluginSlug( $plugin )
334
	{
335
		return substr( $plugin, 0, strrpos( $plugin, '/' ));
336
	}
337
}
338