Passed
Push — main ( 44ea53...137754 )
by TARIQ
15:15 queued 02:39
created

Extension   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 187
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 92
c 1
b 0
f 0
dl 0
loc 187
rs 10
wmc 28

7 Methods

Rating   Name   Duplication   Size   Complexity  
A addDebugBarPanel() 0 5 2
A preAjaxRequest() 0 10 2
A enqueuePanelDependencies() 0 13 1
A removeHooks() 0 4 1
A __construct() 0 14 3
C ajaxCheckNow() 0 73 13
A getLibraryUrl() 0 25 6
1
<?php
2
namespace YahnisElsts\PluginUpdateChecker\v5p0\DebugBar;
3
4
use YahnisElsts\PluginUpdateChecker\v5p0\PucFactory;
5
use YahnisElsts\PluginUpdateChecker\v5p0\UpdateChecker;
6
7
if ( !class_exists(Extension::class, false) ):
8
9
	class Extension {
10
		const RESPONSE_BODY_LENGTH_LIMIT = 4000;
11
12
		/** @var UpdateChecker */
13
		protected $updateChecker;
14
		protected $panelClass = Panel::class;
15
16
		public function __construct($updateChecker, $panelClass = null) {
17
			$this->updateChecker = $updateChecker;
18
			if ( isset($panelClass) ) {
19
				$this->panelClass = $panelClass;
20
			}
21
22
			if ( (strpos($this->panelClass, '\\') === false) ) {
23
				$this->panelClass = __NAMESPACE__ . '\\' . $this->panelClass;
24
			}
25
26
			add_filter('debug_bar_panels', array($this, 'addDebugBarPanel'));
27
			add_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies'));
28
29
			add_action('wp_ajax_puc_v5_debug_check_now', array($this, 'ajaxCheckNow'));
30
		}
31
32
		/**
33
		 * Register the PUC Debug Bar panel.
34
		 *
35
		 * @param array $panels
36
		 * @return array
37
		 */
38
		public function addDebugBarPanel($panels) {
39
			if ( $this->updateChecker->userCanInstallUpdates() ) {
40
				$panels[] = new $this->panelClass($this->updateChecker);
41
			}
42
			return $panels;
43
		}
44
45
		/**
46
		 * Enqueue our Debug Bar scripts and styles.
47
		 */
48
		public function enqueuePanelDependencies() {
49
			wp_enqueue_style(
50
				'puc-debug-bar-style-v5',
51
				$this->getLibraryUrl("/css/puc-debug-bar.css"),
52
				array('debug-bar'),
53
				'20221008'
54
			);
55
56
			wp_enqueue_script(
57
				'puc-debug-bar-js-v5',
58
				$this->getLibraryUrl("/js/debug-bar.js"),
59
				array('jquery'),
60
				'20221008'
61
			);
62
		}
63
64
		/**
65
		 * Run an update check and output the result. Useful for making sure that
66
		 * the update checking process works as expected.
67
		 */
68
		public function ajaxCheckNow() {
69
			//phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce is checked in preAjaxRequest().
70
			if ( !isset($_POST['uid']) || ($_POST['uid'] !== $this->updateChecker->getUniqueName('uid')) ) {
71
				return;
72
			}
73
			$this->preAjaxRequest();
74
			$update = $this->updateChecker->checkForUpdates();
75
			if ( $update !== null ) {
76
				echo "An update is available:";
77
				//phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r -- For debugging output.
78
				echo '<pre>', esc_html(print_r($update, true)), '</pre>';
79
			} else {
80
				echo 'No updates found.';
81
			}
82
83
			$errors = $this->updateChecker->getLastRequestApiErrors();
84
			if ( !empty($errors) ) {
85
				printf('<p>The update checker encountered %d API error%s.</p>', count($errors), (count($errors) > 1) ? 's' : '');
86
87
				foreach (array_values($errors) as $num => $item) {
88
					$wpError = $item['error'];
89
					/** @var \WP_Error $wpError */
90
					printf('<h4>%d) %s</h4>', intval($num + 1), esc_html($wpError->get_error_message()));
91
92
					echo '<dl>';
93
					printf('<dt>Error code:</dt><dd><code>%s</code></dd>', esc_html($wpError->get_error_code()));
94
95
					if ( isset($item['url']) ) {
96
						printf('<dt>Requested URL:</dt><dd><code>%s</code></dd>', esc_html($item['url']));
97
					}
98
99
					if ( isset($item['httpResponse']) ) {
100
						if ( is_wp_error($item['httpResponse']) ) {
101
							$httpError = $item['httpResponse'];
102
							/** @var \WP_Error $httpError */
103
							printf(
104
								'<dt>WordPress HTTP API error:</dt><dd>%s (<code>%s</code>)</dd>',
105
								esc_html($httpError->get_error_message()),
106
								esc_html($httpError->get_error_code())
107
							);
108
						} else {
109
							//Status code.
110
							printf(
111
								'<dt>HTTP status:</dt><dd><code>%d %s</code></dd>',
112
								esc_html(wp_remote_retrieve_response_code($item['httpResponse'])),
113
								esc_html(wp_remote_retrieve_response_message($item['httpResponse']))
114
							);
115
116
							//Headers.
117
							echo '<dt>Response headers:</dt><dd><pre>';
118
							foreach (wp_remote_retrieve_headers($item['httpResponse']) as $name => $value) {
119
								printf("%s: %s\n", esc_html($name), esc_html($value));
120
							}
121
							echo '</pre></dd>';
122
123
							//Body.
124
							$body = wp_remote_retrieve_body($item['httpResponse']);
125
							if ( $body === '' ) {
126
								$body = '(Empty response.)';
127
							} else if ( strlen($body) > self::RESPONSE_BODY_LENGTH_LIMIT ) {
128
								$length = strlen($body);
129
								$body = substr($body, 0, self::RESPONSE_BODY_LENGTH_LIMIT)
130
									. sprintf("\n(Long string truncated. Total length: %d bytes.)", $length);
131
							}
132
133
							printf('<dt>Response body:</dt><dd><pre>%s</pre></dd>', esc_html($body));
134
						}
135
					}
136
					echo '<dl>';
137
				}
138
			}
139
140
			exit;
141
		}
142
143
		/**
144
		 * Check access permissions and enable error display (for debugging).
145
		 */
146
		protected function preAjaxRequest() {
147
			if ( !$this->updateChecker->userCanInstallUpdates() ) {
148
				die('Access denied');
149
			}
150
			check_ajax_referer('puc-ajax');
151
152
			//phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting -- Part of a debugging feature.
153
			error_reporting(E_ALL);
154
			//phpcs:ignore WordPress.PHP.IniSet.display_errors_Blacklisted
155
			@ini_set('display_errors', 'On');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for ini_set(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

155
			/** @scrutinizer ignore-unhandled */ @ini_set('display_errors', 'On');

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...
156
		}
157
158
		/**
159
		 * Remove hooks that were added by this extension.
160
		 */
161
		public function removeHooks() {
162
			remove_filter('debug_bar_panels', array($this, 'addDebugBarPanel'));
163
			remove_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies'));
164
			remove_action('wp_ajax_puc_v5_debug_check_now', array($this, 'ajaxCheckNow'));
165
		}
166
167
		/**
168
		 * @param string $filePath
169
		 * @return string
170
		 */
171
		private function getLibraryUrl($filePath) {
172
			$absolutePath = realpath(dirname(__FILE__) . '/../../../' . ltrim($filePath, '/'));
173
174
			//Where is the library located inside the WordPress directory structure?
175
			$absolutePath = PucFactory::normalizePath($absolutePath);
176
177
			$pluginDir = PucFactory::normalizePath(WP_PLUGIN_DIR);
178
			$muPluginDir = PucFactory::normalizePath(WPMU_PLUGIN_DIR);
179
			$themeDir = PucFactory::normalizePath(get_theme_root());
180
181
			if ( (strpos($absolutePath, $pluginDir) === 0) || (strpos($absolutePath, $muPluginDir) === 0) ) {
182
				//It's part of a plugin.
183
				return plugins_url(basename($absolutePath), $absolutePath);
184
			} else if ( strpos($absolutePath, $themeDir) === 0 ) {
185
				//It's part of a theme.
186
				$relativePath = substr($absolutePath, strlen($themeDir) + 1);
187
				$template = substr($relativePath, 0, strpos($relativePath, '/'));
188
				$baseUrl = get_theme_root_uri($template);
189
190
				if ( !empty($baseUrl) && $relativePath ) {
191
					return $baseUrl . '/' . $relativePath;
192
				}
193
			}
194
195
			return '';
196
		}
197
	}
198
199
endif;
200