Completed
Push — master ( 02e362...3c05a4 )
by Fulvio
04:00
created

Plugin   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 357
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 0
loc 357
rs 9.0399
c 0
b 0
f 0
wmc 42
lcom 1
cbo 8

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 31 5
A connect() 0 27 5
B apply_options() 0 31 8
F init() 0 94 15
A print_notice_exception() 0 10 1
A get_plugin_path() 0 4 1
A get_plugin_vendor_path() 0 4 1
A get_plugin_page_url() 0 4 1
A get_project_page_url() 0 4 1
A get_php_console_project_page_url() 0 4 1
A get_php_console_chrome_extension_url() 0 4 1
A get_reviews_page_url() 0 4 1
A get_support_page_url() 0 4 1

How to fix   Complexity   

Complex Class

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

1
<?php
2
/**
3
 * WP PHP Console
4
 *
5
 * This source file is subject to the GNU General Public License v3.0
6
 * that is bundled with this package in the file license.txt.
7
 * It is also available through the world-wide-web at this URL:
8
 * http://www.gnu.org/licenses/gpl-3.0.html
9
 *
10
 * @author    Fulvio Notarstefano <[email protected]>
11
 * @copyright Copyright (c) 2014-2019 Fulvio Notarstefano
12
 * @license   http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0
13
 */
14
15
namespace WP_PHP_Console;
16
17
use PhpConsole;
18
19
defined( 'ABSPATH' ) or exit;
20
21
/**
22
 * WP PHP Console main class.
23
 *
24
 * @since 1.0.0
25
 */
26
class Plugin {
27
28
29
	/** @var string plugin version */
30
	CONST VERSION = '1.6.0';
31
32
	/** @var string plugin ID */
33
	CONST ID = 'wp-php-console';
34
35
	/** @var string plugin name */
36
	CONST NAME = 'WP PHP Console';
37
38
39
	/** @var PhpConsole\Connector instance */
40
	public $connector;
41
42
43
	/**
44
	 * Loads plugin and connects to PHP Console.
45
	 *
46
	 * @since 1.0.0
47
	 */
48
	public function __construct() {
49
50
		@error_reporting( E_ALL );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
51
52
		foreach ( [ 'WP_DEBUG',	'WP_DEBUG_LOG', 'WP_DEBUG_DISPLAY', ] as $wp_debug_constant ) {
53
			if ( ! defined( $wp_debug_constant ) ) {
54
				define ( $wp_debug_constant, true );
55
			}
56
		}
57
58
		// handle translations
59
		add_action( 'plugins_loaded', static function() {
60
			load_plugin_textdomain(
61
				'wp-php-console',
62
				false,
63
				dirname( dirname( plugin_basename( __FILE__ ) ) ) . '/languages/'
64
			);
65
		} );
66
67
		if ( class_exists( 'PhpConsole\Connector' ) ) {
68
			// connect to PHP Console
69
			add_action( 'init',      [ $this, 'connect' ], -1000 );
70
			// delay further PHP Console initialisation to have more context during Remote PHP execution
71
			add_action( 'wp_loaded', [ $this, 'init' ], -1000 );
72
		}
73
74
		// load admin
75
		if ( is_admin() ) {
76
			new Admin();
77
		}
78
	}
79
80
81
	/**
82
	 * Connects to PHP Console.
83
	 *
84
	 * PHP Console needs to hook in session, in WordPress we need to be in 'init':
85
	 * @link http://silvermapleweb.com/using-the-php-session-in-wordpress/
86
	 *
87
	 * @internal action hook callback
88
	 *
89
	 * @since 1.4.0
90
	 */
91
	public function connect() {
92
93
		// workaround for avoiding headers already sent warnings
94
		@error_reporting( E_ALL & ~E_WARNING );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
95
96
		if ( ! @session_id() ) {
97
			@session_start();
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
98
		}
99
100
		$connected = true;
101
102
		if ( ! $this->connector instanceof PhpConsole\Connector ) {
103
			try {
104
				$this->connector = PhpConsole\Connector::getInstance();
105
			} catch ( \Exception $e ) {
106
				$connected = false;
107
			}
108
		}
109
110
		// restore error reporting
111
		@error_reporting( E_ALL );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
112
113
		// apply PHP Console options
114
		if ( $connected ) {
115
			$this->apply_options();
116
		}
117
	}
118
119
120
	/**
121
	 * Applies options.
122
	 *
123
	 * @since 1.4.0
124
	 */
125
	private function apply_options() {
126
127
		// bail out if not connected yet to PHP Console
128
		if ( ! $this->connector instanceof PhpConsole\Connector ) {
129
			return;
130
		}
131
132
		// apply 'register' option to PHP Console...
133
		if ( Settings::should_register_pc_class() && ! class_exists( 'PC', false ) ) {
134
			// ...only if PC not registered yet
135
			try {
136
				PhpConsole\Helper::register();
137
			} catch( \Exception $e ) {
138
				$this->print_notice_exception( $e );
139
			}
140
		}
141
142
		// apply 'stack' option to PHP Console
143
		if ( Settings::should_show_call_stack() ) {
144
			$this->connector->getDebugDispatcher()->detectTraceAndSource = true;
145
		}
146
147
		// apply 'short' option to PHP Console
148
		if ( Settings::should_use_short_path_names() ) {
149
			try {
150
				$this->connector->setSourcesBasePath( $_SERVER['DOCUMENT_ROOT'] );
151
			} catch ( \Exception $e ) {
152
				$this->print_notice_exception( $e );
153
			}
154
		}
155
	}
156
157
158
	/**
159
	 * Initializes PHP Console.
160
	 *
161
	 * @internal action hook callback
162
	 *
163
	 * @since 1.0.0
164
	 */
165
	public function init() {
166
167
		// bail if no password is set to connect with PHP Console
168
		if ( ! Settings::has_eval_terminal_password() ) {
169
			return;
170
		}
171
172
		// selectively remove slashes added by WordPress as expected by PHP Console
173
		if ( array_key_exists( PhpConsole\Connector::POST_VAR_NAME, $_POST ) ) {
174
			$_POST[ PhpConsole\Connector::POST_VAR_NAME ] = stripslashes_deep( $_POST[ PhpConsole\Connector::POST_VAR_NAME ] );
175
		}
176
177
		// get PHP Console instance if wasn't set yet
178
		if ( ! $this->connector instanceof PhpConsole\Connector ) {
179
180
			// workaround for avoiding headers already sent warnings
181
			@error_reporting( E_ALL & ~E_WARNING );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
182
183
			try {
184
				$this->connector = PhpConsole\Connector::getInstance();
185
				$connected       = true;
186
			} catch ( \Exception $e ) {
187
				$connected       = false;
188
			}
189
190
			// restore error reporting
191
			@error_reporting( E_ALL );
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
192
193
			if ( ! $connected ) {
194
				return;
195
			}
196
		}
197
198
		// set PHP Console password
199
		try {
200
			$this->connector->setPassword( Settings::get_eval_terminal_password() );
201
		} catch ( \Exception $e ) {
202
			$this->print_notice_exception( $e );
203
		}
204
205
		// get PHP Console handler instance
206
		$handler = PhpConsole\Handler::getInstance();
207
208
		if ( true !== PhpConsole\Handler::getInstance()->isStarted() ) {
209
			try {
210
				$handler->start();
211
			} catch( \Exception $e ) {
212
				$this->print_notice_exception( $e );
213
				return;
214
			}
215
		}
216
217
		// enable SSL-only mode
218
		if ( Settings::should_use_ssl_only() ) {
219
			$this->connector->enableSslOnlyMode();
220
		}
221
222
		// restrict IP addresses
223
		$allowedIpMasks = Settings::get_allowed_ip_masks();
224
225
		if ( count( $allowedIpMasks ) > 0 ) {
226
			$this->connector->setAllowedIpMasks( $allowedIpMasks );
227
		}
228
229
		$evalProvider = $this->connector->getEvalDispatcher()->getEvalProvider();
230
231
		try {
232
			$evalProvider->addSharedVar( 'uri', $_SERVER['REQUEST_URI'] );
233
		} catch ( \Exception $e ) {
234
			$this->print_notice_exception( $e );
235
		}
236
237
		try {
238
			$evalProvider->addSharedVarReference( 'post', $_POST );
239
		} catch ( \Exception $e ) {
240
			$this->print_notice_exception( $e );
241
		}
242
243
		$openBaseDirs = [ ABSPATH, get_template_directory() ];
244
245
		try {
246
			$evalProvider->addSharedVarReference( 'dirs', $openBaseDirs );
247
		} catch ( \Exception $e ) {
248
			$this->print_notice_exception( $e );
249
		}
250
251
		$evalProvider->setOpenBaseDirs( $openBaseDirs );
252
253
		try {
254
			$this->connector->startEvalRequestsListener();
255
		} catch ( \Exception $e ) {
256
			$this->print_notice_exception( $e );
257
		}
258
	}
259
260
261
	/**
262
	 * Prints an exception message as WordPress admin notice.
263
	 *
264
	 * @since 1.4.0
265
	 *
266
	 * @param \Exception $e Exception object
267
	 */
268
	private function print_notice_exception( \Exception $e ) {
269
270
		add_action( 'admin_notices', static function() use ( $e ) {
271
			?>
272
			<div class="error">
273
				<p><?php printf( '%1$s: %2$s', self::NAME, $e->getMessage() ); ?></p>
274
			</div>
275
			<?php
276
		} );
277
	}
278
279
280
	/**
281
	 * Gets the plugin path.
282
	 *
283
	 * @since 1.6.0
284
	 *
285
	 * @return string
286
	 */
287
	public static function get_plugin_path() {
288
289
		return untrailingslashit( dirname( __DIR__ ) );
290
	}
291
292
293
	/**
294
	 * Gets the plugin vendor path.
295
	 *
296
	 * @since 1.6.0
297
	 */
298
	public static function get_plugin_vendor_path() {
299
300
		return self::get_plugin_path() . '/vendor';
301
	}
302
303
304
	/**
305
	 * Gets the plugin page URL.
306
	 *
307
	 * @since 1.6.0
308
	 *
309
	 * @return string
310
	 */
311
	public static function get_plugin_page_url() {
312
313
		return 'https://wordpress.org/support/plugin/wp-php-console/';
314
	}
315
316
317
	/**
318
	 * Gets the GitHub repository page URL.
319
	 *
320
	 * @since 1.6.0
321
	 *
322
	 * @return string
323
	 */
324
	public static function get_project_page_url() {
325
326
		return 'https://github.com/unfulvio/wp-php-console';
327
	}
328
329
330
	/**
331
	 * Gets the PHP Console project page URL.
332
	 *
333
	 * @since 1.6.0
334
	 *
335
	 * @return string
336
	 */
337
	public static function get_php_console_project_page_url() {
338
339
		return 'https://github.com/barbushin/php-console';
340
	}
341
342
343
	/**
344
	 * Gets the PHP Console Google Chrome extension URL.
345
	 *
346
	 * @since 1.6.0
347
	 *
348
	 * @return string;
0 ignored issues
show
Documentation introduced by
The doc-type string; could not be parsed: Expected "|" or "end of type", but got ";" at position 6. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
349
	 */
350
	public static function get_php_console_chrome_extension_url() {
351
352
		return 'https://chrome.google.com/webstore/detail/php-console/nfhmhhlpfleoednkpnnnkolmclajemef';
353
	}
354
355
356
	/**
357
	 * Gets the plugin reviews page URL.
358
	 *
359
	 * @since 1.6.0
360
	 *
361
	 * @return string
362
	 */
363
	public static function get_reviews_page_url() {
364
365
		return 'https://wordpress.org/support/plugin/wp-php-console/reviews/';
366
	}
367
368
369
	/**
370
	 * Gets the plugin support page URL.
371
	 *
372
	 * @since 1.6.0
373
	 *
374
	 * @return string
375
	 */
376
	public static function get_support_page_url() {
377
378
		return 'https://wordpress.org/support/plugin/wp-php-console/';
379
	}
380
381
382
}
383