PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.8.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.8.0
4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Backend / Modules / SystemInfo.php
wp-staging / Backend / Modules Last commit date
Jobs 1 year ago Views 2 years ago SystemInfo.php 1 year ago
SystemInfo.php
934 lines
1 <?php
2
3 namespace WPStaging\Backend\Modules;
4
5 use WPStaging\Backend\Upgrade\Upgrade;
6 use WPStaging\Backup\Ajax\FileList\ListableBackupsCollection;
7 use WPStaging\Core\Utils\Browser;
8 use WPStaging\Core\WPStaging;
9 use WPStaging\Core\Utils\Multisite;
10 use WPStaging\Framework\Utils\Urls;
11 use WPStaging\Framework\Adapter\Database;
12 use WPStaging\Framework\BackgroundProcessing\Queue;
13 use WPStaging\Framework\Facades\Sanitize;
14 use WPStaging\Framework\Staging\Sites;
15 use WPStaging\Framework\SiteInfo;
16 use WPStaging\Framework\Database\WpOptionsInfo;
17 use WPStaging\Backup\BackupScheduler;
18
19 // No Direct Access
20 if (!defined("WPINC")) {
21 die;
22 }
23
24 /**
25 * Class SystemInfo
26 * @package WPStaging\Backend\Modules
27 */
28 class SystemInfo
29 {
30 /**
31 * @var string
32 */
33 const REMOVED_LABEL = '[REMOVED]';
34
35 /**
36 * @var string
37 */
38 const NOT_SET_LABEL = '[not set]';
39
40 /**
41 * @var bool
42 */
43 private $isMultiSite;
44
45 /** @var mixed|Database */
46 private $database;
47
48 /**
49 * @var Urls
50 */
51 private $urlsHelper;
52
53 /**
54 * @var WpOptionsInfo
55 */
56 private $wpOptionsInfo;
57
58 public function __construct()
59 {
60 $this->isMultiSite = is_multisite();
61 $this->urlsHelper = WPStaging::make(Urls::class);
62 $this->database = WPStaging::make(Database::class);
63 $this->wpOptionsInfo = WPStaging::make(WpOptionsInfo::class);
64 }
65
66 /**
67 * Magic method
68 * @return string
69 */
70 public function __toString()
71 {
72 return $this->get();
73 }
74
75 /**
76 * Get System Information as text
77 * @return string
78 */
79 public function get(): string
80 {
81 $output = $this->server();
82 $output .= $this->php();
83 $output .= $this->wp();
84 $output .= $this->getMultisiteInfo();
85 $output .= $this->wpstaging();
86 $output .= $this->plugins();
87 $output .= $this->multiSitePlugins();
88 $output .= $this->phpExtensions();
89 $output .= $this->browser();
90 $output .= PHP_EOL . "### End System Info ###";
91
92 return $output;
93 }
94
95 /**
96 * @param string $string
97 * @return string
98 */
99 public function header(string $string): string
100 {
101 return PHP_EOL . "### {$string} ###" . PHP_EOL . PHP_EOL;
102 }
103
104 /**
105 * Formatting title and the value
106 * @param string $title
107 * @param string|array $value
108 * @return string
109 */
110 public function info(string $title, $value): string
111 {
112 return str_pad($title, 56, ' ', STR_PAD_RIGHT) . print_r($value, true) . PHP_EOL;
113 }
114
115 /**
116 * WordPress Configuration
117 * @return string
118 */
119 public function wp(): string
120 {
121 $output = $this->header("WordPress");
122 $output .= $this->info("Site:", ($this->isMultiSite) ? 'Multi Site' : 'Single Site');
123 $output .= $this->info("WP Version:", get_bloginfo("version"));
124 $output .= $this->info("Installed in subdir:", ($this->isSubDir() ? 'Yes' : 'No'));
125 $output .= $this->info("Database Name:", $this->database->getWpdb()->dbname);
126 $output .= $this->info("Table Prefix:", $this->getTablePrefix());
127 $output .= $this->info("site_url():", site_url());
128 $output .= $this->info("home_url():", $this->urlsHelper->getHomeUrl());
129 $output .= $this->info("get_home_path():", get_home_path());
130 $output .= $this->info("ABSPATH:", ABSPATH);
131
132 $permissions = fileperms(ABSPATH);
133
134 $output .= $this->info("ABSPATH Fileperms:", $permissions);
135
136 $permissions = substr(sprintf('%o', $permissions), -4);
137
138 $output .= $this->info("ABSPATH Permissions:", $permissions);
139
140 $absPathStat = stat(ABSPATH);
141 if (!$absPathStat) {
142 $absPathStat = "";
143 } else {
144 $absPathStat = json_encode($absPathStat);
145 }
146
147 $output .= $this->info("ABSPATH Stat:", $absPathStat);
148 $output .= $this->constantInfo('WP_PLUGIN_DIR');
149 $output .= $this->constantInfo('WP_CONTENT_DIR');
150
151 $output .= $this->info("Is wp-content link:", is_link(WP_CONTENT_DIR) ? 'Yes' : 'No');
152 if (is_link(WP_CONTENT_DIR)) {
153 $output .= $this->info("wp-content link target:", readlink(WP_CONTENT_DIR));
154 $output .= $this->info("wp-content realpath:", realpath(WP_CONTENT_DIR));
155 }
156
157 $output .= $this->constantInfo('UPLOADS');
158
159 $uploads = wp_upload_dir();
160 $output .= $this->info("uploads['path']:", $uploads['path']);
161 $output .= $this->info("uploads['subdir']:", $uploads['subdir']);
162 $output .= $this->info("uploads['basedir']:", $uploads['basedir']);
163 $output .= $this->info("uploads['baseurl']:", $uploads['baseurl']);
164 $output .= $this->info("uploads['url']:", $uploads['url']);
165
166 $output .= $this->info("UPLOAD_PATH in wp-config.php:", (defined("UPLOAD_PATH")) ? UPLOAD_PATH : self::NOT_SET_LABEL);
167 $output .= $this->info("upload_path in " . $this->database->getPrefix() . 'options:', get_option("upload_path") ?: self::NOT_SET_LABEL);
168 $output .= $this->getPrimaryKeyInfo();
169
170 $output .= $this->constantInfo('WP_TEMP_DIR');
171
172 $output .= $this->info("WP_DEBUG:", (defined("WP_DEBUG")) ? (WP_DEBUG ? "Enabled" : "Disabled") : self::NOT_SET_LABEL);
173 $output .= $this->constantInfo('WP_MEMORY_LIMIT');
174 $output .= $this->constantInfo('WP_MAX_MEMORY_LIMIT');
175 $output .= $this->info("Active Theme:", $this->theme());
176 $output .= $this->info("Permalink Structure:", get_option("permalink_structure") ?: "Default");
177 $output .= $this->wpRemotePost();
178 $output .= $this->info("WPLANG:", (defined("WPLANG") && WPLANG) ? WPLANG : "en_US");
179 $output .= $this->info("Wordpress cron:", wp_json_encode(get_option('cron', [])));
180
181 return $output;
182 }
183
184 /**
185 * Theme Information
186 * @return string
187 */
188 public function theme(): string
189 {
190 // Versions earlier than 3.4
191 if (get_bloginfo("version") < "3.4") {
192 $themeData = get_theme_data(get_stylesheet_directory() . "/style.css");
193 return "{$themeData["Name"]} {$themeData["Version"]}";
194 }
195
196 $themeData = wp_get_theme();
197 return "{$themeData->Name} {$themeData->Version}";
198 }
199
200 /**
201 * Multisite information
202 * @return string
203 */
204 private function getMultisiteInfo(): string
205 {
206 if (!$this->isMultiSite) {
207 return '';
208 }
209
210 $multisite = new Multisite();
211
212 $output = $this->info("Multisite:", "Yes");
213 $output .= $this->info("Multisite Blog ID:", get_current_blog_id());
214 $output .= $this->info("MultiSite URL:", $multisite->getHomeURL());
215 $output .= $this->info("MultiSite URL without scheme:", $multisite->getHomeUrlWithoutScheme());
216 $output .= $this->info("MultiSite is Main Site:", is_main_site() ? 'Yes' : 'No');
217
218 $output .= $this->constantInfo('SUBDOMAIN_INSTALL');
219 $output .= $this->constantInfo('DOMAIN_CURRENT_SITE');
220 $output .= $this->constantInfo('PATH_CURRENT_SITE');
221 $output .= $this->constantInfo('SITE_ID_CURRENT_SITE');
222 $output .= $this->constantInfo('BLOG_ID_CURRENT_SITE');
223
224 $networkSites = get_sites();
225 $output .= PHP_EOL . $this->info("Network Sites:", count($networkSites)) . PHP_EOL;
226 foreach ($networkSites as $site) {
227 $siteDetails = get_blog_details($site->blog_id);
228 if (!$siteDetails) {
229 continue;
230 }
231
232 $output .= $this->info("Blog ID:", $site->blog_id);
233 $output .= $this->info("Home URL:", get_home_url($site->blog_id));
234 $output .= $this->info("Site URL:", get_site_url($site->blog_id));
235 $output .= $this->info("Domain:", $site->domain);
236 $output .= $this->info("Path:", $site->path);
237 $output .= PHP_EOL;
238 }
239
240 return $output;
241 }
242
243 /**
244 * Wp Staging plugin Information
245 * @return string
246 */
247 public function wpstaging(): string
248 {
249 $settings = (object)get_option('wpstg_settings', []);
250 $optionBackupScheduleErrorReport = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_ERROR_REPORT);
251 $optionBackupScheduleReportEmail = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_REPORT_EMAIL);
252 $optionBackupScheduleSlackErrorReport = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_SLACK_ERROR_REPORT);
253 $optionBackupScheduleReportSlackWebhook = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_REPORT_SLACK_WEBHOOK);
254
255 $output = PHP_EOL . "## WP Staging ##" . PHP_EOL . PHP_EOL;
256
257 $output .= $this->info("Pro Version:", get_option('wpstgpro_version', self::NOT_SET_LABEL));
258 $output .= $this->info("Pro License Key:", get_option('wpstg_license_key') ?: self::NOT_SET_LABEL);
259 // @see \WPStaging\Backend\Pro\Upgrade\Upgrade::OPTION_INSTALL_DATE
260 $output .= $this->info("Pro Install Date:", get_option('wpstgpro_install_date', self::NOT_SET_LABEL));
261 // @see \WPStaging\Backend\Pro\Upgrade\Upgrade::OPTION_UPGRADE_DATE
262 $output .= $this->info("Pro Update Date:", get_option('wpstgpro_upgrade_date', self::NOT_SET_LABEL));
263 $output .= $this->info("Free or Pro Install Date (legacy):", get_option('wpstg_installDate', self::NOT_SET_LABEL));
264 $output .= $this->info("Free Version:", get_option('wpstg_version', self::NOT_SET_LABEL));
265 $output .= $this->info("Free Install Date:", get_option(Upgrade::OPTION_INSTALL_DATE, self::NOT_SET_LABEL));
266 $output .= $this->info("Free Update Date:", get_option(Upgrade::OPTION_UPGRADE_DATE, self::NOT_SET_LABEL));
267 $output .= $this->info("Updated from Pro Version:", get_option('wpstgpro_version_upgraded_from') ?: self::NOT_SET_LABEL);
268 $output .= $this->info("Updated from Free Version:", get_option('wpstg_version_upgraded_from') ?: self::NOT_SET_LABEL);
269 $output .= $this->info("Is Staging Site:", (new SiteInfo())->isStagingSite() ? 'true' : 'false');
270 $output .= $this->getBackupDetails();
271 $output .= $this->getScheduleInfo();
272 $output .= $this->info("DB Query Limit:", isset($settings->queryLimit) ? $settings->queryLimit : self::NOT_SET_LABEL);
273 $output .= $this->info("DB Search & Replace Limit:", isset($settings->querySRLimit) ? $settings->querySRLimit : self::NOT_SET_LABEL);
274 $output .= $this->info("File Copy Limit:", isset($settings->fileLimit) ? $settings->fileLimit : self::NOT_SET_LABEL);
275 $output .= $this->info("Maximum File Size:", isset($settings->maxFileSize) ? $settings->maxFileSize : self::NOT_SET_LABEL);
276 $output .= $this->info("File Copy Batch Size:", isset($settings->batchSize) ? $settings->batchSize : self::NOT_SET_LABEL);
277 $output .= $this->info("CPU Load Priority:", isset($settings->cpuLoad) ? $settings->cpuLoad : self::NOT_SET_LABEL);
278 $output .= $this->info("Keep Permalinks:", isset($settings->keepPermalinks) ? $settings->keepPermalinks : self::NOT_SET_LABEL);
279 $output .= $this->info("Debug Mode:", isset($settings->debugMode) ? $settings->debugMode : self::NOT_SET_LABEL);
280 $output .= $this->info("Optimize Active:", isset($settings->optimizer) ? $settings->optimizer : self::NOT_SET_LABEL);
281 $output .= $this->info("Delete on Uninstall:", isset($settings->unInstallOnDelete) ? $settings->unInstallOnDelete : self::NOT_SET_LABEL);
282 $output .= $this->info("Check Directory Size:", isset($settings->checkDirectorySize) ? $settings->checkDirectorySize : self::NOT_SET_LABEL);
283 $output .= $this->info("Access Permissions:", isset($settings->userRoles) ? $settings->userRoles : self::NOT_SET_LABEL);
284 $output .= $this->info("Users With Staging Access:", isset($settings->usersWithStagingAccess) ? $settings->usersWithStagingAccess : self::NOT_SET_LABEL);
285 $output .= $this->info("Admin Bar Color:", isset($settings->adminBarColor) ? $settings->adminBarColor : self::NOT_SET_LABEL);
286 $analyticsHasConsent = get_option('wpstg_analytics_has_consent');
287 $output .= $this->info("Send Usage Information:", !empty($analyticsHasConsent) ? 'true' : 'false');
288 $output .= $this->info("Send Backup Errors via E-Mail:", !empty($optionBackupScheduleErrorReport) && $optionBackupScheduleErrorReport === 'true' ? 'true' : 'false');
289 $output .= $this->info("E-Mail Address:", !empty($optionBackupScheduleReportEmail) && is_email($optionBackupScheduleReportEmail) ? $optionBackupScheduleReportEmail : self::NOT_SET_LABEL);
290 $output .= $this->info("Send Backup Errors via Slack Webhook:", !empty($optionBackupScheduleSlackErrorReport) && $optionBackupScheduleSlackErrorReport === 'true' ? 'true' : ( WPStaging::isPro() ? 'false' : self::NOT_SET_LABEL ));
291 $output .= $this->info("Slack Webhook URL:", WPStaging::isPro() && !empty($optionBackupScheduleReportSlackWebhook) ? self::REMOVED_LABEL : self::NOT_SET_LABEL);
292 $output .= $this->info("Backup Compression:", isset($settings->enableCompression) ? ($settings->enableCompression ? 'On' : 'Off') : self::NOT_SET_LABEL);
293
294 $output .= PHP_EOL . "-- Google Drive Settings" . PHP_EOL;
295
296 $googleDriveSettings = (array)get_option('wpstg_googledrive', []);
297 if (!empty($googleDriveSettings)) {
298 foreach ($googleDriveSettings as $key => $value) {
299 $output .= $this->info($key, empty($value) ? self::NOT_SET_LABEL : $this->removeCredentials($key, $value));
300 }
301 }
302
303 $output .= PHP_EOL . "-- Amazon S3 Settings" . PHP_EOL;
304
305 $amazonS3Settings = (array)get_option('wpstg_amazons3', []);
306 if (!empty($amazonS3Settings)) {
307 foreach ($amazonS3Settings as $key => $value) {
308 $output .= $this->info($key, empty($value) ? self::NOT_SET_LABEL : $this->removeCredentials($key, $value));
309 }
310 }
311
312 $output .= PHP_EOL . "-- DigitalOcean Spaces Settings" . PHP_EOL;
313
314 $digitalOceanSpacesSettings = (array)get_option('wpstg_digitalocean-spaces', []);
315 if (!empty($digitalOceanSpacesSettings)) {
316 foreach ($digitalOceanSpacesSettings as $key => $value) {
317 $output .= $this->info($key, empty($value) ? self::NOT_SET_LABEL : $this->removeCredentials($key, $value));
318 }
319 }
320
321 $output .= PHP_EOL . "-- Wasabi Settings" . PHP_EOL;
322
323 $wasabiSettings = (array)get_option('wpstg_wasabi-s3', []);
324 if (!empty($wasabiSettings)) {
325 foreach ($wasabiSettings as $key => $value) {
326 $output .= $this->info($key, empty($value) ? self::NOT_SET_LABEL : $this->removeCredentials($key, $value));
327 }
328 }
329
330 $output .= PHP_EOL . "-- Generic S3 Settings" . PHP_EOL;
331
332 $genericS3Settings = (array)get_option('wpstg_generic-s3', []);
333 if (!empty($genericS3Settings)) {
334 foreach ($genericS3Settings as $key => $value) {
335 $output .= $this->info($key, empty($value) ? self::NOT_SET_LABEL : $this->removeCredentials($key, $value));
336 }
337 }
338
339 $output .= PHP_EOL . "-- SFTP Settings" . PHP_EOL;
340
341 $sftpSettings = (array)get_option('wpstg_sftp', []);
342 if (!empty($sftpSettings)) {
343 foreach ($sftpSettings as $key => $value) {
344 $output .= $this->info($key, empty($value) ? self::NOT_SET_LABEL : $this->removeCredentials($key, $value));
345 }
346 }
347
348 $output .= PHP_EOL . "-- Existing Staging Sites" . PHP_EOL . PHP_EOL;
349
350 // Clones data version > 2.x
351 // old name wpstg_existing_clones_beta
352 // New name since version 4.0.3 wpstg_staging_sites
353 $stagingSites = get_option(Sites::STAGING_SITES_OPTION, []);
354 if (is_array($stagingSites)) {
355 foreach ($stagingSites as $key => $clone) {
356 $path = !empty($clone['path']) ? $clone['path'] : self::NOT_SET_LABEL;
357
358 $output .= $this->info("Number:", isset($clone['number']) ? $clone['number'] : self::NOT_SET_LABEL);
359 $output .= $this->info("directoryName:", isset($clone['directoryName']) ? $clone['directoryName'] : self::NOT_SET_LABEL);
360 $output .= $this->info("Path:", $path);
361 $output .= $this->info("URL:", isset($clone['url']) ? $clone['url'] : self::NOT_SET_LABEL);
362 $output .= $this->info("DB Prefix:", isset($clone['prefix']) ? $clone['prefix'] : self::NOT_SET_LABEL);
363 $output .= $this->info("DB Prefix wp-config.php:", $this->getStagingPrefix($clone));
364 $output .= $this->info("WP STAGING Version:", isset($clone['version']) ? $clone['version'] : self::NOT_SET_LABEL);
365 $output .= $this->info("WP Version:", $this->getStagingWpVersion($path)) . PHP_EOL . PHP_EOL;
366
367 if (!empty($clone['databasePassword'])) {
368 $clone['databasePassword'] = self::REMOVED_LABEL;
369 }
370
371 if (!empty($clone['adminPassword'])) {
372 $clone['adminPassword'] = self::REMOVED_LABEL;
373 }
374
375 $stagingSites[$key] = $clone;
376 }
377 }
378
379 $stagingSitesOptionBackup = (array)get_option(Sites::BACKUP_STAGING_SITES_OPTION, []);
380 foreach ($stagingSitesOptionBackup as $key => $clone) {
381 if (!empty($clone['databasePassword'])) {
382 $clone['databasePassword'] = self::REMOVED_LABEL;
383 }
384
385 if (!empty($clone['adminPassword'])) {
386 $clone['adminPassword'] = self::REMOVED_LABEL;
387 }
388
389 $stagingSitesOptionBackup[$key] = $clone;
390 }
391
392 $output .= $this->info(Sites::STAGING_SITES_OPTION . ": ", serialize($stagingSites));
393 $output .= $this->info(Sites::BACKUP_STAGING_SITES_OPTION . ": ", serialize($stagingSitesOptionBackup));
394 $output .= PHP_EOL;
395
396 $output .= "-- Legacy Options" . PHP_EOL . PHP_EOL;
397
398 $output .= $this->info("wpstg_existing_clones: ", serialize(get_option('wpstg_existing_clones')));
399 $output .= $this->info(Sites::OLD_STAGING_SITES_OPTION . ": ", serialize(get_option(Sites::OLD_STAGING_SITES_OPTION, [])));
400
401 return $output;
402 }
403
404 /**
405 * @return string
406 */
407 public function getWpStagingVersion(): string
408 {
409 if (defined('WPSTGPRO_VERSION')) {
410 return 'Pro ' . WPSTGPRO_VERSION;
411 }
412
413 if (defined('WPSTG_VERSION')) {
414 return WPSTG_VERSION;
415 }
416
417 return 'unknown';
418 }
419
420 /**
421 * Browser Information
422 * @return string
423 */
424 public function browser(): string
425 {
426 $output = $this->header("User Browser");
427 $output .= (new Browser());
428
429 return $output;
430 }
431
432 /**
433 * Check wp_remote_post() functionality
434 * @return string
435 */
436 public function wpRemotePost(): string
437 {
438 // Make sure wp_remote_post() is working
439 $wpRemotePost = "does not work";
440
441 // Send request
442 $response = wp_remote_post(
443 "https://www.paypal.com/cgi-bin/webscr",
444 [
445 "sslverify" => false,
446 "timeout" => 60,
447 "user-agent" => "WPSTG/" . WPStaging::getVersion(),
448 "body" => ["cmd" => "_notify-validate"]
449 ]
450 );
451
452 // Validate it worked
453 if (!is_wp_error($response) && $response["response"]["code"] >= 200 && $response["response"]["code"] < 300) {
454 $wpRemotePost = "works";
455 }
456
457 return $this->info("wp_remote_post():", $wpRemotePost);
458 }
459
460 /**
461 * List of Active Plugins
462 * @param array $allAvailablePlugins
463 * @param array $activePlugins
464 * @return string
465 */
466 public function activePlugins(array $allAvailablePlugins, array $activePlugins): string
467 {
468 if ($this->isMultiSite) {
469 $output = $this->header("Active Plugins on this Site");
470 } else {
471 $output = $this->header("Active Plugins");
472 }
473
474 foreach ($allAvailablePlugins as $path => $plugin) {
475 if (!in_array($path, $activePlugins)) {
476 continue;
477 }
478
479 $output .= $this->info($plugin["Name"] . ":", $plugin["Version"]);
480 }
481
482 return $output;
483 }
484
485 /**
486 * List of Inactive Plugins
487 * @param array $allAvailablePlugins
488 * @param array $activePlugins
489 * @return string
490 */
491 public function inactivePlugins(array $allAvailablePlugins, array $activePlugins): string
492 {
493 if ($this->isMultiSite) {
494 $output = $this->header("Inactive Plugins (Includes this and other sites in the same network)");
495 } else {
496 $output = $this->header("Inactive Plugins");
497 }
498
499 foreach ($allAvailablePlugins as $path => $plugin) {
500 if (in_array($path, $activePlugins)) {
501 continue;
502 }
503
504 $output .= $this->info($plugin["Name"] . ":", $plugin["Version"]);
505 }
506
507 return $output;
508 }
509
510 /**
511 * Get list of active and inactive plugins
512 * @return string
513 */
514 public function plugins(): string
515 {
516 // Get plugins and active plugins
517 $allAvailablePlugins = get_plugins();
518 $activePlugins = get_option("active_plugins", []);
519
520 $activePluginsToGetInactive = $activePlugins;
521 if ($this->isMultiSite) {
522 $networkActivePlugins = array_keys(get_site_option("active_sitewide_plugins", []));
523 $activePluginsToGetInactive = array_merge($activePluginsToGetInactive, $networkActivePlugins);
524 }
525
526 // Active plugins
527 $output = $this->activePlugins($allAvailablePlugins, $activePlugins);
528 $output .= $this->inactivePlugins($allAvailablePlugins, $activePluginsToGetInactive);
529
530 return $output;
531 }
532
533 /**
534 * Multisite Plugins
535 * @return string
536 */
537 public function multiSitePlugins(): string
538 {
539 if (!$this->isMultiSite) {
540 return '';
541 }
542
543 $output = $this->header("Active Network Plugins (Includes this and other sites in the same network)");
544
545 $plugins = wp_get_active_network_plugins();
546 $activePlugins = get_site_option("active_sitewide_plugins", []);
547
548 foreach ($plugins as $pluginPath) {
549 $pluginBase = plugin_basename($pluginPath);
550
551 if (!array_key_exists($pluginBase, $activePlugins)) {
552 continue;
553 }
554
555 $plugin = get_plugin_data($pluginPath);
556
557 $output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
558 }
559
560 unset($plugins, $activePlugins);
561
562 return $output;
563 }
564
565 /**
566 * Server Information
567 * @return string
568 */
569 public function server(): string
570 {
571 $output = $this->header("Start System Info");
572 $output .= $this->info("Webserver:", isset($_SERVER["SERVER_SOFTWARE"]) ? Sanitize::sanitizeString($_SERVER["SERVER_SOFTWARE"]) : '');
573 $output .= $this->info("MySQL Server Type:", $this->database->getServerType());
574 $output .= $this->info("MySQL Version:", $this->database->getSqlVersion($compact = true));
575 $output .= $this->info("MySQL Version Full Info:", $this->database->getSqlVersion());
576 $output .= $this->info("PHP Version:", PHP_VERSION);
577
578 return $output;
579 }
580
581 /**
582 * @return string
583 */
584 public function getMySqlServerType(): string
585 {
586 return $this->database->getServerType();
587 }
588
589 /**
590 * @return string
591 */
592 public function getMySqlFullVersion(): string
593 {
594 return $this->database->getSqlVersion();
595 }
596
597 /**
598 * @return string
599 */
600 public function getMySqlVersionCompact(): string
601 {
602 return $this->database->getSqlVersion($compact = true);
603 }
604
605 /**
606 * @return string
607 */
608 public function getPhpVersion(): string
609 {
610 return PHP_VERSION;
611 }
612
613 /**
614 * @return string
615 */
616 public function getWebServerInfo(): string
617 {
618 return isset($_SERVER["SERVER_SOFTWARE"]) ? Sanitize::sanitizeString($_SERVER["SERVER_SOFTWARE"]) : '';
619 }
620
621 /**
622 * PHP Configuration
623 * @return string
624 */
625 public function php(): string
626 {
627 $output = $this->info("PHP memory_limit:", ini_get("memory_limit"));
628 $output .= $this->info("PHP memory_limit in Bytes:", wp_convert_hr_to_bytes(ini_get("memory_limit")));
629 $output .= $this->info("PHP max_execution_time:", ini_get("max_execution_time"));
630 $output .= $this->info("PHP Safe Mode:", ($this->isSafeModeEnabled() ? "Enabled" : "Disabled"));
631 $output .= $this->info("PHP Upload Max File Size:", ini_get("upload_max_filesize"));
632 $output .= $this->info("PHP Post Max Size:", ini_get("post_max_size"));
633 $output .= $this->info("PHP Upload Max Filesize:", ini_get("upload_max_filesize"));
634 $output .= $this->info("PHP Max Input Vars:", ini_get("max_input_vars"));
635 $displayErrors = ini_get("display_errors");
636 $output .= $this->info("PHP display_errors:", ($displayErrors) ? "On ({$displayErrors})" : "N/A");
637 $output .= $this->info("PHP User:", $this->getPHPUser());
638
639 return $output;
640 }
641
642 /**
643 * @return string
644 */
645 public function getPHPUser(): string
646 {
647
648 $user = '';
649
650 if (extension_loaded('posix') && function_exists('posix_getpwuid')) {
651 $file = WPSTG_PLUGIN_DIR . 'Core/WPStaging.php';
652 $user = posix_getpwuid(fileowner($file));
653 return isset($user['name']) ? $user['name'] : 'can not detect PHP user name';
654 }
655
656 if (function_exists('exec') && @exec('echo EXEC') == 'EXEC') {
657 return exec('whoami');
658 }
659
660 return $user;
661 }
662
663 /**
664 * Check if PHP is on Safe Mode
665 * @return bool
666 */
667 public function isSafeModeEnabled(): bool
668 {
669 return (
670 version_compare(PHP_VERSION, "5.4.0", '<') &&
671 // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
672 @ini_get("safe_mode")
673 );
674 }
675
676 /**
677 * Checks if function exists or not
678 * @param string $functionName
679 * @return string
680 */
681 public function isSupported(string $functionName): string
682 {
683 return (function_exists($functionName)) ? "Supported" : "Not Supported";
684 }
685
686 /**
687 * Checks if class or extension is loaded / exists to determine if it is installed or not
688 * @param string $name
689 * @param bool $isClass
690 * @return string
691 */
692 public function isInstalled(string $name, bool $isClass = true): string
693 {
694 if ($isClass === true) {
695 return (class_exists($name)) ? "Installed" : "Not Installed";
696 } else {
697 return (extension_loaded($name)) ? "Installed" : "Not Installed";
698 }
699 }
700
701 /**
702 * Gets Installed Important PHP Extensions
703 * @return string
704 */
705 public function phpExtensions(): string
706 {
707 // Important PHP Extensions
708 $version = function_exists('curl_version') ? curl_version() : ['version' => 'Error: not available', 'ssl_version' => 'Error: not available', 'host' => 'Error: not available', 'protocols' => [], 'features' => []];
709
710 $bitfields = [
711 'CURL_VERSION_IPV6',
712 'CURL_VERSION_KERBEROS4',
713 'CURL_VERSION_SSL',
714 'CURL_VERSION_LIBZ'
715 ];
716
717 $output = $this->header("PHP Extensions");
718
719 $output .= $this->info("cURL:", $this->isSupported("curl_init"));
720 $output .= $this->info("cURL version:", $version['version']);
721 $output .= $this->info("cURL ssl version number:", $version['ssl_version']);
722 $output .= $this->info("cURL host:", $version['host']);
723
724 foreach ($version['protocols'] as $protocols) {
725 $output .= $this->info("cURL protocols:", $protocols);
726 }
727
728 foreach ($bitfields as $feature) {
729 $output .= $this->info($feature . ":", ($version['features'] & constant($feature) ? 'yes' : 'no'));
730 }
731
732
733 $output .= $this->info("fsockopen:", $this->isSupported("fsockopen"));
734 $output .= $this->info("SOAP Client:", $this->isInstalled("SoapClient"));
735 $output .= $this->info("Suhosin:", $this->isInstalled("suhosin", false));
736
737 return $output;
738 }
739
740 /**
741 * Check if WP is installed in subdir
742 * @return bool
743 */
744 private function isSubDir(): bool
745 {
746 // Compare names without scheme to bypass cases where siteurl and home have different schemes http / https
747 // This is happening much more often than you would expect
748 $siteurl = preg_replace('#^https?://#', '', rtrim(get_option('siteurl'), '/'));
749 $home = preg_replace('#^https?://#', '', rtrim(get_option('home'), '/'));
750
751 if ($home !== $siteurl) {
752 return true;
753 }
754
755 return false;
756 }
757
758 /**
759 * Check and return prefix of the staging site
760 */
761
762 /**
763 * Try to get the staging prefix from wp-config.php of staging site
764 * @param array $clone
765 * @return string
766 */
767 private function getStagingPrefix(array $clone = []): string
768 {
769 // Throw error
770 $path = ABSPATH . $clone['directoryName'] . DIRECTORY_SEPARATOR . "wp-config.php";
771
772 if (!file_exists($path)) {
773 return 'File does not exist in: ' . $path;
774 }
775
776 if (($content = @file_get_contents($path)) === false) {
777 return 'Can\'t find staging wp-config.php';
778 } else {
779 // Get prefix from wp-config.php
780 //preg_match_all("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
781 preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
782 //wp_die(var_dump($matches));
783
784 if (!empty($matches[1])) {
785 return $matches[1];
786 } else {
787 return 'No table_prefix in wp-config.php';
788 }
789 }
790 }
791
792 /**
793 * Get staging site wordpress version number
794 * @param string $path
795 * @return string
796 */
797 private function getStagingWpVersion(string $path): string
798 {
799
800 if ($path === self::NOT_SET_LABEL) {
801 return "Error: Cannot detect WP version";
802 }
803
804 // Get version number of wp staging
805 $file = trailingslashit($path) . 'wp-includes/version.php';
806
807 if (!file_exists($file)) {
808 return "Error: Cannot detect WP version. File does not exist: $file";
809 }
810
811 $version = @file_get_contents($file);
812
813 $versionStaging = empty($version) ? 'unknown' : $version;
814
815 preg_match("/\\\$wp_version.*=.*'(.*)';/", $versionStaging, $matches);
816
817 if (empty($matches[1])) {
818 return "Error: Cannot detect WP version";
819 }
820
821 return $matches[1];
822 }
823
824 /**
825 * @param $key
826 * @param $value
827 * @return mixed|string
828 */
829 private function removeCredentials($key, $value)
830 {
831 $protectedFields = ['accessToken', 'refreshToken', 'accessKey', 'secretKey', 'password', 'passphrase'];
832 if (!empty($value) && in_array($key, $protectedFields)) {
833 return self::REMOVED_LABEL;
834 }
835
836 return empty($value) ? self::NOT_SET_LABEL : $value;
837 }
838
839 /**
840 * @return string
841 */
842 private function getScheduleInfo(): string
843 {
844 $output = '';
845 $backupSchedules = get_option('wpstg_backup_schedules', []);
846 if (!empty($backupSchedules)) {
847 foreach ($backupSchedules as $key => $value) {
848 $output .= $this->info('Schedule ' . !empty($key) ? $key : '', empty($value) ? self::NOT_SET_LABEL : print_r($value, true));
849 }
850 } else {
851 $output .= $this->info('wpstg_backup_schedules ', self::NOT_SET_LABEL);
852 }
853
854 /** @var Queue */
855 $queue = WPStaging::make(Queue::class);
856
857 $output .= $this->info("Backup All Actions in DB:", $queue->count());
858 $output .= $this->info("Backup Pending Actions (ready):", $queue->count(Queue::STATUS_READY));
859 $output .= $this->info("Backup Processing Actions (processing):", $queue->count(Queue::STATUS_PROCESSING));
860 $output .= $this->info("Backup Completed Actions (completed):", $queue->count(Queue::STATUS_COMPLETED));
861 $output .= $this->info("Backup Failed Actions (failed):", $queue->count(Queue::STATUS_FAILED));
862
863 return $output;
864 }
865
866 /**
867 * @return string
868 */
869 private function getTablePrefix(): string
870 {
871 $tablePrefix = "DB Prefix: " . $this->database->getPrefix() . ' ';
872 $tablePrefix .= "Length: " . strlen($this->database->getPrefix()) . " Status: ";
873 $tablePrefix .= (strlen($this->database->getPrefix()) > 16) ? " ERROR: Too long" : " Acceptable";
874
875 return $tablePrefix;
876 }
877
878 /**
879 * @return string
880 */
881 private function getBackupDetails(): string
882 {
883 $backups = WPStaging::make(ListableBackupsCollection::class)->getListableBackups();
884
885 $output = $this->info("Number of Backups:", count($backups));
886
887 $totalBackupSize = 0;
888 foreach ($backups as $backup) {
889 $totalBackupSize += (float)$backup->size;
890 }
891
892 $output .= $this->info("Backup Total File Size:", esc_html($totalBackupSize) . 'M');
893
894 return $output;
895 }
896
897 /**
898 * @param string $constantName
899 * @return string
900 */
901 protected function constantInfo(string $constantName): string
902 {
903 if (!defined($constantName)) {
904 return $this->info($constantName . ':', self::NOT_SET_LABEL);
905 }
906
907 $constantValue = constant($constantName);
908 if (is_bool($constantValue)) {
909 $constantValue = $constantValue ? 'Yes' : 'No';
910 }
911
912 return $this->info($constantName . ':', $constantValue);
913 }
914
915 /**
916 * @return string
917 */
918 private function getPrimaryKeyInfo(): string
919 {
920 $tableName = $this->database->getPrefix() . 'options';
921 $isPrimaryKeyMissing = $this->wpOptionsInfo->isOptionTablePrimaryKeyMissing($tableName);
922 if ($isPrimaryKeyMissing) {
923 return $this->info("{$tableName} primary key:", self::NOT_SET_LABEL);
924 }
925
926 $isPrimaryKeyIsOptionName = $this->wpOptionsInfo->isPrimaryKeyIsOptionName($tableName);
927 if ($isPrimaryKeyIsOptionName) {
928 return $this->info("{$tableName} primary key:", 'option_name');
929 }
930
931 return $this->info("{$tableName} primary key:", 'option_id');
932 }
933 }
934