PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.0
4.11.0 4.10.0 4.9.5 4.9.4 4.9.3 4.9.2 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 day ago Views 1 day ago SystemInfo.php 1 day ago SystemInfoParser.php 1 day ago
SystemInfo.php
1188 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\Facades\Info;
11 use WPStaging\Framework\Utils\Urls;
12 use WPStaging\Framework\Adapter\Database;
13 use WPStaging\Framework\BackgroundProcessing\Queue;
14 use WPStaging\Framework\Facades\Sanitize;
15 use WPStaging\Notifications\Notifications;
16 use WPStaging\Staging\Sites;
17 use WPStaging\Framework\SiteInfo;
18 use WPStaging\Framework\Database\WpOptionsInfo;
19 use WPStaging\Framework\Security\DataEncryption;
20 use WPStaging\Backup\BackupScheduler;
21
22
23 if (!defined("WPINC")) {
24 die;
25 }
26
27
28
29
30
31 class SystemInfo
32 {
33
34
35
36 const REMOVED_LABEL = '[REMOVED]';
37
38
39
40
41 const NOT_SET_LABEL = '[not set]';
42
43
44
45
46 private $isMultiSite;
47
48
49
50
51 private $database;
52
53
54
55
56 private $urlsHelper;
57
58
59
60
61 private $wpOptionsInfo;
62
63
64
65
66 private $isEncodeProLicense = false;
67
68
69 private $siteInfo;
70
71
72
73
74 private $enableStructuredOutput = false;
75
76
77
78
79 private $structuredData = [];
80
81
82
83
84 private $currentSection = null;
85
86 public function __construct()
87 {
88 $this->isMultiSite = is_multisite();
89 $this->urlsHelper = WPStaging::make(Urls::class);
90 $this->database = WPStaging::make(Database::class);
91 $this->wpOptionsInfo = WPStaging::make(WpOptionsInfo::class);
92 $this->siteInfo = WPStaging::make(SiteInfo::class);
93 }
94
95 public function __toString(): string
96 {
97 return $this->get();
98 }
99
100 public function setStructuredOutput(bool $enable = true)
101 {
102 $this->enableStructuredOutput = $enable;
103 if ($enable) {
104 $this->structuredData = [];
105 }
106 }
107
108 public function getSections(): array
109 {
110 $this->setStructuredOutput(true);
111 $this->get();
112 return $this->getStructuredDataWithDisplayNames();
113 }
114
115 public function get(): string
116 {
117 $output = $this->server();
118 $output .= $this->php();
119 $output .= $this->wp();
120 $output .= $this->getMultisiteInfo();
121 $output .= $this->wpstaging();
122 $output .= $this->plugins();
123 $output .= $this->muPlugins();
124 $output .= $this->dropIns();
125 $output .= $this->multiSitePlugins();
126 $output .= $this->phpExtensions();
127 $output .= $this->browser();
128 $output .= PHP_EOL . "### End System Info ###";
129
130 return $output;
131 }
132
133 public function header(string $string): string
134 {
135 $this->currentSection = $string;
136 return PHP_EOL . "### {$string} ###" . PHP_EOL . PHP_EOL;
137 }
138
139
140
141
142 public function info(string $title, $value): string
143 {
144
145 if ($this->enableStructuredOutput) {
146 if (!isset($this->structuredData[$this->currentSection])) {
147 $this->structuredData[$this->currentSection] = [];
148 }
149
150 $this->structuredData[$this->currentSection][] = [
151 'label' => rtrim($title, ':'),
152 'value' => $value,
153 ];
154 }
155
156 return str_pad($title, 56, ' ', STR_PAD_RIGHT) . print_r($value, true) . PHP_EOL;
157 }
158
159
160
161
162
163
164 public function getStructuredDataWithDisplayNames(): array
165 {
166 $data = [];
167 foreach ($this->structuredData as $sectionId => $items) {
168 $displayName = SystemInfoParser::getDisplayName($sectionId);
169 $data[$displayName] = $items;
170 }
171
172 return $data;
173 }
174
175
176
177
178
179 public function wp(): string
180 {
181
182 $this->currentSection = SystemInfoParser::SECTIONS['WORDPRESS_ENVIRONMENT']['id'];
183 $output = $this->info("Site Type:", ($this->isMultiSite) ? 'Multi Site' : 'Single Site');
184 $output .= $this->info("WordPress Version:", get_bloginfo("version"));
185 $output .= $this->info("Installed in Subdirectory:", ($this->isSubDir() ? 'Yes' : 'No'));
186 $output .= $this->info("WP_DEBUG:", (defined("WP_DEBUG")) ? (WP_DEBUG ? "Enabled" : "Disabled") : self::NOT_SET_LABEL);
187 $output .= $this->info("WPLANG:", (defined("WPLANG") && WPLANG) ? WPLANG : "en_US");
188 $output .= $this->wpRemotePost();
189
190
191 $this->currentSection = SystemInfoParser::SECTIONS['URLS_PATHS']['id'];
192 $output .= $this->info("Site URL:", site_url());
193 $output .= $this->info("Home URL:", $this->urlsHelper->getHomeUrl());
194 $output .= $this->info("Home Path:", get_home_path());
195 $output .= $this->info("ABSPATH:", ABSPATH);
196 $permissions = fileperms(ABSPATH);
197 if ($permissions !== false) {
198 $output .= $this->info("ABSPATH Fileperms:", (string)$permissions);
199 $permissions = substr(sprintf('%o', $permissions), -4);
200 $output .= $this->info("ABSPATH Permissions:", $permissions);
201 } else {
202 $output .= $this->info("ABSPATH Permissions:", "N/A");
203 }
204
205 $absPathStat = stat(ABSPATH);
206 if (!$absPathStat) {
207 $absPathStat = "";
208 }
209
210 if ($this->enableStructuredOutput) {
211 $output .= $this->info("ABSPATH Stat:", $absPathStat);
212 } else {
213 $output .= $this->info("ABSPATH Stat:", json_encode($absPathStat));
214 }
215
216
217 $this->currentSection = SystemInfoParser::SECTIONS['WORDPRESS_DIRECTORIES']['id'];
218 $output .= $this->constantInfo('WP_CONTENT_DIR');
219 $output .= $this->constantInfo('WP_PLUGIN_DIR');
220 $output .= $this->info("Is wp-content Symlink:", is_link(WP_CONTENT_DIR) ? 'Yes' : 'No');
221 $output .= $this->info("Symlinks Disabled:", Info::canUse('symlink') === false ? 'Yes' : 'No');
222 if (is_link(WP_CONTENT_DIR)) {
223 $output .= $this->info("wp-content link target:", readlink(WP_CONTENT_DIR));
224 $output .= $this->info("wp-content realpath:", realpath(WP_CONTENT_DIR));
225 }
226
227 $output .= $this->constantInfo('WP_TEMP_DIR');
228
229
230 $this->currentSection = SystemInfoParser::SECTIONS['MEDIA_UPLOADS']['id'];
231 $output .= $this->constantInfo('UPLOADS');
232 $uploads = wp_upload_dir();
233 $output .= $this->info("Uploads Base Dir:", $uploads['basedir']);
234 $output .= $this->info("Uploads URL:", $uploads['url']);
235 $output .= $this->info("Uploads Path:", $uploads['path']);
236 $output .= $this->info("Uploads Subdir:", $uploads['subdir']);
237 $output .= $this->info("Uploads Base URL:", $uploads['baseurl']);
238 $output .= $this->info("UPLOADS Constant:", (defined("UPLOADS")) ? UPLOADS : self::NOT_SET_LABEL);
239 $output .= $this->info("UPLOAD_PATH (wp-config.php):", (defined("UPLOAD_PATH")) ? UPLOAD_PATH : self::NOT_SET_LABEL);
240 $tableName = $this->database->getPrefix() . 'options';
241 $output .= $this->info("upload_path ($tableName):", get_option("upload_path") ?: self::NOT_SET_LABEL);
242
243
244 $this->currentSection = SystemInfoParser::SECTIONS['WORDPRESS_MEMORY_SETTINGS']['id'];
245 $output .= $this->constantInfo('WP_MEMORY_LIMIT');
246 $output .= $this->constantInfo('WP_MAX_MEMORY_LIMIT');
247
248
249 $this->currentSection = SystemInfoParser::SECTIONS['FILESYSTEM_PERMISSIONS']['id'];
250 $output .= $this->constantInfo('FS_CHMOD_DIR');
251 $output .= $this->constantInfo('FS_CHMOD_FILE');
252
253
254 $this->currentSection = SystemInfoParser::SECTIONS['THEME_PERMALINKS']['id'];
255 $settings = (object)get_option('wpstg_settings', []);
256 $output .= $this->info("Active Theme:", $this->theme());
257 $output .= $this->info("Permalink Structure:", get_option("permalink_structure") ?: "Default");
258 $output .= $this->info("Keep Permalinks:", isset($settings->keepPermalinks) ? $settings->keepPermalinks : self::NOT_SET_LABEL);
259
260
261 $this->currentSection = SystemInfoParser::SECTIONS['WORDPRESS_CRON_JOBS']['id'];
262 $cron = get_option('cron', []);
263 $output .= $this->info("WP-Cron Enabled:", (!defined('DISABLE_WP_CRON') || !DISABLE_WP_CRON) ? 'Yes' : 'No');
264 $output .= $this->info("Scheduled Events:", !empty($cron) ? count($cron) . ' events' : 'No events');
265 if ($this->enableStructuredOutput) {
266 $output .= $this->info("Wordpress cron:", $cron);
267 } else {
268 $output .= $this->info("Wordpress cron:", wp_json_encode($cron));
269 }
270
271 $backupSchedules = get_option('wpstg_backup_schedules', []);
272 if (!empty($backupSchedules)) {
273 $output .= $this->info("Backup Schedule:", $backupSchedules);
274 } else {
275 $output .= $this->info('Backup Schedule:', self::NOT_SET_LABEL);
276 }
277
278 return $output;
279 }
280
281
282
283
284
285 public function theme(): string
286 {
287
288 if (get_bloginfo("version") < "3.4") {
289 $themeData = get_theme_data(get_stylesheet_directory() . "/style.css");
290 return "{$themeData["Name"]} (v{$themeData["Version"]})";
291 }
292
293 $themeData = wp_get_theme();
294 return "{$themeData->Name} (v{$themeData->Version})";
295 }
296
297
298
299
300
301 private function getMultisiteInfo(): string
302 {
303 if (!$this->isMultiSite) {
304 return '';
305 }
306
307 $this->currentSection = SystemInfoParser::SECTIONS['MULTISITE']['id'];
308 $multisite = new Multisite();
309
310 $output = $this->info("Multisite:", "Yes");
311 $output .= $this->info("Multisite Blog ID:", (string)get_current_blog_id());
312 $output .= $this->info("MultiSite URL:", $multisite->getHomeURL());
313 $output .= $this->info("MultiSite URL without scheme:", $multisite->getHomeUrlWithoutScheme());
314 $output .= $this->info("MultiSite is Main Site:", is_main_site() ? 'Yes' : 'No');
315
316 $output .= $this->constantInfo('SUBDOMAIN_INSTALL');
317 $output .= $this->constantInfo('DOMAIN_CURRENT_SITE');
318 $output .= $this->constantInfo('PATH_CURRENT_SITE');
319 $output .= $this->constantInfo('SITE_ID_CURRENT_SITE');
320 $output .= $this->constantInfo('BLOG_ID_CURRENT_SITE');
321
322 $networkSites = get_sites();
323 if ($this->enableStructuredOutput) {
324 $output .= $this->info("Network Sites", $networkSites);
325 return $output;
326 }
327
328 $output .= PHP_EOL . $this->info("Network Sites:", count($networkSites)) . PHP_EOL;
329 foreach ($networkSites as $site) {
330 $siteDetails = get_blog_details($site->blog_id);
331 if (!$siteDetails) {
332 continue;
333 }
334
335 $output .= $this->info("Blog ID:", $site->blog_id);
336 $output .= $this->info("Home URL:", get_home_url($site->blog_id));
337 $output .= $this->info("Site URL:", get_site_url($site->blog_id));
338 $output .= $this->info("Domain:", $site->domain);
339 $output .= $this->info("Path:", $site->path);
340 $output .= PHP_EOL;
341 }
342
343 return $output;
344 }
345
346
347
348
349
350 public function wpstaging(): string
351 {
352 $settings = (object)get_option('wpstg_settings', []);
353 $optionBackupScheduleErrorReport = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_ERROR_REPORT);
354 $optionBackupScheduleWarningReport = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_WARNING_REPORT);
355 $optionBackupScheduleGeneralReport = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_GENERAL_REPORT);
356 $optionBackupScheduleReportEmail = get_option(Notifications::OPTION_BACKUP_SCHEDULE_REPORT_EMAIL);
357 $optionBackupScheduleSlackErrorReport = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_SLACK_ERROR_REPORT);
358 $optionBackupScheduleReportSlackWebhook = get_option(BackupScheduler::OPTION_BACKUP_SCHEDULE_REPORT_SLACK_WEBHOOK);
359 $wpStagingFreeVersion = wpstgGetPluginData('wp-staging.php');
360 $output = PHP_EOL . "## WP Staging ##" . PHP_EOL . PHP_EOL;
361
362
363 $this->currentSection = SystemInfoParser::SECTIONS['WP_STAGING_PLUGIN_INFO']['id'];
364 $output .= $this->info("Pro License Key:", $this->getLicenseKey() ?: self::NOT_SET_LABEL);
365 $output .= $this->info("Pro Version:", get_option('wpstgpro_version', self::NOT_SET_LABEL));
366 $output .= $this->info("Pro Install Date:", get_option('wpstgpro_install_date', self::NOT_SET_LABEL));
367 $output .= $this->info("Updated from Pro Version:", get_option('wpstgpro_version_upgraded_from') ?: self::NOT_SET_LABEL);
368 $output .= $this->info("Pro Update Date:", get_option('wpstgpro_upgrade_date', self::NOT_SET_LABEL));
369 $output .= $this->info("Free Version:", empty($wpStagingFreeVersion['Version']) ? self::NOT_SET_LABEL : $wpStagingFreeVersion['Version']);
370 $output .= $this->info("Free Install Date:", get_option(Upgrade::OPTION_INSTALL_DATE, self::NOT_SET_LABEL));
371 $output .= $this->info("Free Update Date:", get_option(Upgrade::OPTION_UPGRADE_DATE, self::NOT_SET_LABEL));
372 $output .= $this->info("Updated from Free Version:", get_option('wpstg_version_upgraded_from') ?: self::NOT_SET_LABEL);
373 $output .= $this->info("Free or Pro Install Date (legacy):", get_option('wpstg_installDate', self::NOT_SET_LABEL));
374 $output .= $this->info("Is Staging Site:", $this->siteInfo->isStagingSite() ? 'Yes' : 'No');
375
376 $this->currentSection = SystemInfoParser::SECTIONS['WP_STAGING_BACKUP_STATUS']['id'];
377 $output .= $this->getBackupDetails();
378 $output .= $this->getQueueInfo();
379
380
381 $this->currentSection = SystemInfoParser::SECTIONS['WP_STAGING_PERFORMANCE']['id'];
382 $output .= $this->info("DB Query Limit:", $this->getSettingValue($settings, 'queryLimit', true));
383 $output .= $this->info("Search & Replace Limit:", $this->getSettingValue($settings, 'querySRLimit', true));
384 $output .= $this->info("File Copy Limit:", $this->getSettingValue($settings, 'fileLimit', true));
385 $output .= $this->info("Maximum File Size:", $this->getSettingValue($settings, 'maxFileSize'));
386 $output .= $this->info("File Copy Batch Size:", $this->getSettingValue($settings, 'batchSize'));
387 $cpuLoad = $this->getSettingValue($settings, 'cpuLoad');
388 $output .= $this->info("CPU Load Priority:", $cpuLoad !== self::NOT_SET_LABEL ? ucfirst(strtolower($cpuLoad)) : $cpuLoad);
389 $output .= $this->info("Optimizer Enabled:", isset($settings->optimizer) && $settings->optimizer ? 'Yes' : 'No');
390 $output .= $this->info("Backup Compression:", isset($settings->enableCompression) ? ($settings->enableCompression ? 'On' : 'Off') : self::NOT_SET_LABEL);
391 $output .= $this->info("Debug Mode Enabled:", isset($settings->debugMode) && $settings->debugMode ? 'Yes' : 'No');
392
393 $this->currentSection = SystemInfoParser::SECTIONS['WP_STAGING_ACCESS']['id'];
394 $userRoles = isset($settings->userRoles) ? $settings->userRoles : [];
395 if (is_array($userRoles) && !empty($userRoles)) {
396 $rolesList = implode(', ', array_map('ucfirst', $userRoles));
397 } else {
398 $rolesList = self::NOT_SET_LABEL;
399 }
400
401 $output .= $this->info("Allowed Roles:", $rolesList);
402 $usersWithAccess = isset($settings->usersWithStagingAccess) ? $settings->usersWithStagingAccess : [];
403 if (is_array($usersWithAccess) && !empty($usersWithAccess)) {
404 $usersList = implode(', ', $usersWithAccess);
405 } else {
406 $usersList = 'Not listed';
407 }
408
409 $output .= $this->info("Users with Staging Access:", $usersList);
410 $output .= $this->info("Delete Data on Uninstall:", isset($settings->unInstallOnDelete) ? ($settings->unInstallOnDelete ? 'Yes' : 'No') : self::NOT_SET_LABEL);
411 $output .= $this->info("Send Usage Information:", !empty(get_option('wpstg_analytics_has_consent')) ? 'true' : 'false');
412 $output .= $this->info("Send Backup Errors via Email:", $this->formatBooleanOption($optionBackupScheduleErrorReport));
413 $output .= $this->info("Send Backup Warnings via Email:", $this->formatBooleanOption($optionBackupScheduleWarningReport));
414 $output .= $this->info("Send Backup General Report via Email:", $this->formatBooleanOption($optionBackupScheduleGeneralReport));
415 $output .= $this->info("Email Address:", !empty($optionBackupScheduleReportEmail) && is_email($optionBackupScheduleReportEmail) ? $optionBackupScheduleReportEmail : self::NOT_SET_LABEL);
416 $output .= $this->info("Send Backup Errors via Slack Webhook:", $this->formatBooleanOption($optionBackupScheduleSlackErrorReport) === 'true' ? 'true' : (WPStaging::isPro() ? 'false' : self::NOT_SET_LABEL));
417 $output .= $this->info("Slack Webhook URL:", WPStaging::isPro() && !empty($optionBackupScheduleReportSlackWebhook) ? self::REMOVED_LABEL : self::NOT_SET_LABEL);
418
419 $this->currentSection = SystemInfoParser::SECTIONS['WP_STAGING_STORAGE_PROVIDER']['id'];
420
421 $parser = WPStaging::make(SystemInfoParser::class);
422 $storageProviders = $parser->getStorageProvidersForSystemInfo();
423 foreach ($storageProviders as $provider) {
424 $output .= $this->formatStorageSettings($provider['optionName'], $provider['title']);
425 }
426
427 $this->currentSection = SystemInfoParser::SECTIONS['WP_STAGING_EXISTING_SITES']['id'];
428 if (!$this->enableStructuredOutput) {
429 $output .= PHP_EOL . "-- Existing Staging Sites" . PHP_EOL . PHP_EOL;
430 }
431
432 $stagingSites = get_option(Sites::STAGING_SITES_OPTION, []);
433 if (is_array($stagingSites)) {
434 foreach ($stagingSites as $key => $clone) {
435 $path = !empty($clone['path']) ? $clone['path'] : self::NOT_SET_LABEL;
436 if ($this->enableStructuredOutput) {
437 $clone['wpVersion'] = $this->getStagingWpVersion($path);
438 $stagingSites[$key] = $clone;
439 continue;
440 }
441
442 $output .= $this->info("Number:", isset($clone['number']) ? $clone['number'] : self::NOT_SET_LABEL);
443 $output .= $this->info("directoryName:", isset($clone['directoryName']) ? $clone['directoryName'] : self::NOT_SET_LABEL);
444 $output .= $this->info("Path:", $path);
445 $output .= $this->info("URL:", isset($clone['url']) ? $clone['url'] : self::NOT_SET_LABEL);
446 $output .= $this->info("DB Prefix:", isset($clone['prefix']) ? $clone['prefix'] : self::NOT_SET_LABEL);
447 $output .= $this->info("DB Prefix wp-config.php:", $this->getStagingPrefix($clone));
448 $output .= $this->info("WP STAGING Version:", isset($clone['version']) ? $clone['version'] : self::NOT_SET_LABEL);
449 $output .= $this->info("WP Version:", $this->getStagingWpVersion($path)) . PHP_EOL . PHP_EOL;
450 }
451 }
452
453 $stagingSites = $this->sanitizeSitePasswords(is_array($stagingSites) ? $stagingSites : []);
454 $stagingSitesOptionBackup = $this->sanitizeSitePasswords((array)get_option(Sites::BACKUP_STAGING_SITES_OPTION, []));
455
456 $output .= $this->info(Sites::STAGING_SITES_OPTION . ": ", serialize($stagingSites));
457 $output .= $this->info(Sites::BACKUP_STAGING_SITES_OPTION . ": ", serialize($stagingSitesOptionBackup));
458 return $output;
459 }
460
461 public function getWpStagingVersion(): string
462 {
463 if (defined('WPSTGPRO_VERSION')) {
464 return 'Pro ' . WPSTGPRO_VERSION;
465 }
466
467 if (defined('WPSTG_VERSION')) {
468 return WPSTG_VERSION;
469 }
470
471 return 'unknown';
472 }
473
474
475
476
477
478 public function browser(): string
479 {
480 $this->currentSection = SystemInfoParser::SECTIONS['CLIENT_BROWSER_INFO']['id'];
481 $output = $this->header("Client / Browser Information");
482 $browser = new Browser();
483 $browserInfo = (string)$browser;
484
485
486
487 if ($this->enableStructuredOutput) {
488 $lines = explode("\n", trim($browserInfo));
489 foreach ($lines as $line) {
490 $line = trim($line);
491 if (empty($line)) {
492 continue;
493 }
494
495
496 if (preg_match('/^(.{1,56})\s+(.+)$/', $line, $matches)) {
497 $label = trim($matches[1]);
498 $value = trim($matches[2]);
499 $this->info($label, $value);
500 } else {
501 $this->info('', $line);
502 }
503 }
504 }
505
506 $output .= $browserInfo;
507 return $output;
508 }
509
510
511
512
513
514 public function wpRemotePost(): string
515 {
516
517 $wpRemotePost = "does not work";
518
519
520
521 $hostName = 'www.paypal.com';
522 $hostIp = gethostbyname($hostName);
523 if (preg_match('@\.0$@', $hostIp)) {
524 return $this->info("wp_remote_post():", $wpRemotePost);
525 }
526
527
528 $response = wp_remote_post(
529 "https://" . $hostName . "/cgi-bin/webscr",
530 [
531 "sslverify" => false,
532 "timeout" => 60,
533 "user-agent" => "WPSTG/" . WPStaging::getVersion(),
534 "body" => ["cmd" => "_notify-validate"],
535 ]
536 );
537
538
539 if (!is_wp_error($response) && $response["response"]["code"] >= 200 && $response["response"]["code"] < 300) {
540 $wpRemotePost = "works";
541 }
542
543 return $this->info("wp_remote_post():", $wpRemotePost);
544 }
545
546
547
548
549
550
551
552 public function activePlugins(array $allAvailablePlugins, array $activePlugins): string
553 {
554 $this->currentSection = SystemInfoParser::SECTIONS['PLUGINS_OVERVIEW']['id'];
555 $output = $this->header("Active Plugins");
556
557 foreach ($allAvailablePlugins as $path => $plugin) {
558 if (!in_array($path, $activePlugins)) {
559 continue;
560 }
561
562 $output .= $this->info($plugin["Name"] . ":", $plugin["Version"]);
563 }
564
565 return $output;
566 }
567
568
569
570
571
572
573
574 public function inactivePlugins(array $allAvailablePlugins, array $activePlugins): string
575 {
576 $this->currentSection = SystemInfoParser::SECTIONS['PLUGINS_OVERVIEW']['id'];
577 if ($this->isMultiSite) {
578 $output = $this->header("Inactive Plugins (Includes this and other sites in the same network)");
579 } else {
580 $output = $this->header("Inactive Plugins");
581 }
582
583 foreach ($allAvailablePlugins as $path => $plugin) {
584 if (in_array($path, $activePlugins)) {
585 continue;
586 }
587
588 $output .= $this->info($plugin["Name"] . ":", $plugin["Version"]);
589 }
590
591 return $output;
592 }
593
594
595
596
597
598 public function plugins(): string
599 {
600
601 $allAvailablePlugins = get_plugins();
602 $activePlugins = get_option("active_plugins", []);
603
604 $activePluginsToGetInactive = $activePlugins;
605 if ($this->isMultiSite) {
606 $networkActivePlugins = array_keys(get_site_option("active_sitewide_plugins", []));
607 $activePluginsToGetInactive = array_merge($activePluginsToGetInactive, $networkActivePlugins);
608 }
609
610
611 $output = $this->activePlugins($allAvailablePlugins, $activePlugins);
612 $output .= $this->inactivePlugins($allAvailablePlugins, $activePluginsToGetInactive);
613
614 return $output;
615 }
616
617
618
619
620
621 public function multiSitePlugins(): string
622 {
623 if (!$this->isMultiSite) {
624 return '';
625 }
626
627 $this->currentSection = SystemInfoParser::SECTIONS['PLUGINS_OVERVIEW']['id'];
628 $output = $this->header("Active Network Plugins (Includes this and other sites in the same network)");
629
630 $plugins = wp_get_active_network_plugins();
631 $activePlugins = get_site_option("active_sitewide_plugins", []);
632
633 foreach ($plugins as $pluginPath) {
634 $pluginBase = plugin_basename($pluginPath);
635
636 if (!array_key_exists($pluginBase, $activePlugins)) {
637 continue;
638 }
639
640 $plugin = get_plugin_data($pluginPath);
641
642 $output .= "{$plugin["Name"]}: {$plugin["Version"]}" . PHP_EOL;
643 }
644
645 unset($plugins, $activePlugins);
646
647 return $output;
648 }
649
650
651
652
653
654 public function server(): string
655 {
656
657 $this->currentSection = SystemInfoParser::SECTIONS['SERVER_AND_OS']['id'];
658 $output = $this->header("Server & Operating System");
659 $output .= $this->info("Web Server:", isset($_SERVER["SERVER_SOFTWARE"]) ? Sanitize::sanitizeString($_SERVER["SERVER_SOFTWARE"]) : '');
660 $output .= $this->info("OS Architecture:", $this->siteInfo->getOSArchitecture());
661 $output .= $this->info("Server User:", $this->getPHPUser());
662
663
664 switch ($this->database->getLowerTablesNameSettings()) {
665 case '0':
666 $lowerTablesNameSettings = 'case-sensitive';
667 break;
668 case '1':
669 case '2':
670 $lowerTablesNameSettings = 'case-insensitive';
671 break;
672 default:
673 $lowerTablesNameSettings = 'N/A';
674 }
675
676
677 $this->currentSection = SystemInfoParser::SECTIONS['DATABASE_MYSQL_MARIADB']['id'];
678 $output .= $this->info("Database Type:", $this->database->getServerType());
679 $output .= $this->info("Version:", $this->database->getSqlVersion($compact = true));
680 $output .= $this->info("Full Version:", $this->database->getSqlVersion());
681 $output .= $this->info("Database Name:", $this->database->getWpdb()->dbname);
682 $output .= $this->info("Table Prefix:", $this->getTablePrefix());
683 $output .= $this->info("lower_case_table_names:", $lowerTablesNameSettings);
684 $output .= $this->getPrimaryKeyInfo();
685
686
687 $this->currentSection = SystemInfoParser::SECTIONS['PHP_ENVIRONMENT']['id'];
688 $output .= $this->info("PHP Version:", PHP_VERSION);
689 $output .= $this->info("PHP Architecture:", $this->siteInfo->getPhpArchitecture());
690 $output .= $this->info("PHP Safe Mode:", ($this->isSafeModeEnabled() ? "Enabled" : "Disabled"));
691 $displayErrors = ini_get("display_errors");
692 $output .= $this->info("display_errors:", ($displayErrors) ? "On ({$displayErrors})" : "N/A");
693
694 return $output;
695 }
696
697
698
699
700 public function getMySqlServerType(): string
701 {
702 return $this->database->getServerType();
703 }
704
705
706
707
708 public function getMySqlFullVersion(): string
709 {
710 return $this->database->getSqlVersion();
711 }
712
713
714
715
716 public function getMySqlVersionCompact(): string
717 {
718 return $this->database->getSqlVersion($compact = true);
719 }
720
721
722
723
724 public function getPhpVersion(): string
725 {
726 return PHP_VERSION;
727 }
728
729
730
731
732 public function getWebServerInfo(): string
733 {
734 return isset($_SERVER["SERVER_SOFTWARE"]) ? Sanitize::sanitizeString($_SERVER["SERVER_SOFTWARE"]) : '';
735 }
736
737
738
739
740
741 public function php(): string
742 {
743
744 $this->currentSection = SystemInfoParser::SECTIONS['PHP_LIMITS']['id'];
745 $memoryLimit = ini_get("memory_limit");
746 $output = $this->info("memory_limit:", $memoryLimit . ' (' . number_format(wp_convert_hr_to_bytes($memoryLimit)) . ' bytes)');
747 $output .= $this->info("max_execution_time:", ini_get("max_execution_time"));
748 $output .= $this->info("max_input_vars:", ini_get("max_input_vars"));
749 $output .= $this->info("upload_max_filesize:", ini_get("upload_max_filesize"));
750 $output .= $this->info("post_max_size:", ini_get("post_max_size"));
751
752 return $output;
753 }
754
755
756
757
758 public function getPHPUser(): string
759 {
760
761 $user = '';
762
763 if (extension_loaded('posix') && function_exists('posix_getpwuid')) {
764 $file = WPSTG_PLUGIN_DIR . 'Core/WPStaging.php';
765 $user = posix_getpwuid(fileowner($file));
766 return isset($user['name']) ? $user['name'] : 'can not detect PHP user name';
767 }
768
769 if (function_exists('exec') && in_array('exec', explode(',', ini_get('disable_functions')))) {
770 $user = exec('whoami');
771 }
772
773 return empty($user) ? 'can not detect PHP user name' : $user;
774 }
775
776
777
778
779
780 public function isSafeModeEnabled(): bool
781 {
782 return (
783 version_compare(PHP_VERSION, "5.4.0", '<') &&
784 // phpcs:ignore PHPCompatibility.IniDirectives.RemovedIniDirectives.safe_modeDeprecatedRemoved
785 @ini_get("safe_mode")
786 );
787 }
788
789
790
791
792
793
794 public function isSupported(string $functionName): string
795 {
796 return (function_exists($functionName)) ? "Supported" : "Not Supported";
797 }
798
799
800
801
802
803
804
805 public function isInstalled(string $name, bool $isClass = true): string
806 {
807 if ($isClass === true) {
808 return (class_exists($name)) ? "Installed" : "Not Installed";
809 } else {
810 return (extension_loaded($name)) ? "Installed" : "Not Installed";
811 }
812 }
813
814
815
816
817
818 public function phpExtensions(): string
819 {
820
821 $version = function_exists('curl_version') ? curl_version() : ['version' => 'Error: not available', 'ssl_version' => 'Error: not available', 'host' => 'Error: not available', 'protocols' => [], 'features' => []];
822
823 $bitfields = [
824 'CURL_VERSION_IPV6',
825 'CURL_VERSION_KERBEROS4',
826 'CURL_VERSION_SSL',
827 'CURL_VERSION_LIBZ',
828 ];
829
830 $this->currentSection = SystemInfoParser::SECTIONS['CURL_ENVIRONMENT']['id'];
831 $output = $this->header("cURL Environment");
832
833 $output .= $this->info("cURL Support:", $this->isSupported("curl_init") ? 'Yes' : 'No');
834 $output .= $this->info("cURL Version:", $version['version']);
835 $output .= $this->info("cURL Host:", $version['host']);
836 $output .= $this->info("SSL Library:", $version['ssl_version']);
837 $this->currentSection = SystemInfoParser::SECTIONS['CURL_FEATURES']['id'];
838 foreach ($bitfields as $feature) {
839 $output .= $this->info($feature . ":", (defined($feature) && $version['features'] & constant($feature) ? 'Supported' : 'Not Supported'));
840 }
841
842 $httpProtocols = ['http', 'https', 'ftp', 'ftps', 'file', 'dict', 'gopher'];
843 $emailProtocols = ['imap', 'imaps', 'pop3', 'pop3s', 'smtp', 'smtps'];
844 $allProtocols = $version['protocols'];
845
846 $httpProtocolsFound = array_values(array_intersect($allProtocols, $httpProtocols));
847 $emailProtocolsFound = array_values(array_intersect($allProtocols, $emailProtocols));
848
849 $otherProtocols = array_values(array_diff(
850 $allProtocols,
851 $httpProtocols,
852 $emailProtocols
853 ));
854 $this->currentSection = SystemInfoParser::SECTIONS['SUPPORTED_PROTOCOLS']['id'];
855 if (!empty($httpProtocolsFound)) {
856 $output .= $this->info('HTTP Protocols:', implode(', ', $httpProtocolsFound));
857 }
858
859 if (!empty($emailProtocolsFound)) {
860 $output .= $this->info('Email Protocols:', implode(', ', $emailProtocolsFound));
861 }
862
863 if (!empty($otherProtocols)) {
864 $output .= $this->info('Other Protocols:', implode(', ', $otherProtocols));
865 }
866
867 $this->currentSection = SystemInfoParser::SECTIONS['PHP_NETWORK_EXTENSIONS']['id'];
868 $output .= $this->info("fsockopen:", $this->isSupported("fsockopen") ? 'Supported' : 'Not Supported');
869 $output .= $this->info("SOAP Client:", $this->isInstalled("SoapClient"));
870 $output .= $this->info("Suhosin:", $this->isInstalled("suhosin", false));
871
872 return $output;
873 }
874
875
876
877
878
879 private function isSubDir(): bool
880 {
881
882
883 $siteurl = preg_replace('#^https?://#', '', rtrim(get_option('siteurl'), '/'));
884 $home = preg_replace('#^https?://#', '', rtrim(get_option('home'), '/'));
885
886 if ($home !== $siteurl) {
887 return true;
888 }
889
890 return false;
891 }
892
893
894
895
896
897
898 private function getStagingPrefix(array $clone = []): string
899 {
900
901 $path = ABSPATH . $clone['directoryName'] . DIRECTORY_SEPARATOR . "wp-config.php";
902
903 if (!file_exists($path)) {
904 return 'File does not exist in: ' . $path;
905 }
906
907 if (($content = @file_get_contents($path)) === false) {
908 return 'Can\'t find staging wp-config.php';
909 } else {
910
911
912 preg_match("/table_prefix\s*=\s*'(\w*)';/", $content, $matches);
913
914
915 if (!empty($matches[1])) {
916 return $matches[1];
917 } else {
918 return 'No table_prefix in wp-config.php';
919 }
920 }
921 }
922
923
924
925
926
927
928 private function getStagingWpVersion(string $path): string
929 {
930
931 if ($path === self::NOT_SET_LABEL) {
932 return "Error: Cannot detect WP version";
933 }
934
935
936 $file = trailingslashit($path) . 'wp-includes/version.php';
937
938 if (!file_exists($file)) {
939 return "Error: Cannot detect WP version. File does not exist: $file";
940 }
941
942 $version = @file_get_contents($file);
943
944 $versionStaging = empty($version) ? 'unknown' : $version;
945
946 preg_match("/\\\$wp_version.*=.*'(.*)';/", $versionStaging, $matches);
947
948 if (empty($matches[1])) {
949 return "Error: Cannot detect WP version";
950 }
951
952 return $matches[1];
953 }
954
955
956
957
958
959
960 private function removeCredentials($key, $value)
961 {
962 $protectedFields = ['accessToken', 'refreshToken', 'accessKey', 'secretKey', 'password', 'passphrase'];
963 if (!empty($value) && in_array($key, $protectedFields)) {
964 return self::REMOVED_LABEL;
965 }
966
967 return empty($value) ? self::NOT_SET_LABEL : $value;
968 }
969
970
971
972
973 private function getQueueInfo(): string
974 {
975 $output = '';
976
977
978 $queue = WPStaging::make(Queue::class);
979
980 $output .= $this->info("Backup All Actions in DB:", (string)$queue->count());
981 $output .= $this->info("Pending Actions:", (string)$queue->count(Queue::STATUS_READY));
982 $output .= $this->info("Processing Actions:", (string)$queue->count(Queue::STATUS_PROCESSING));
983 $output .= $this->info("Completed Actions:", (string)$queue->count(Queue::STATUS_COMPLETED));
984 $output .= $this->info("Failed Actions:", (string)$queue->count(Queue::STATUS_FAILED));
985
986 return $output;
987 }
988
989
990
991
992 private function getTablePrefix(): string
993 {
994 $prefix = $this->database->getPrefix();
995 $length = strlen($prefix);
996 $status = ($length > 16) ? "ERROR: Too long" : "Acceptable";
997 return $prefix . ' (Length: ' . $length . '' . $status . ')';
998 }
999
1000
1001
1002
1003 private function getBackupDetails(): string
1004 {
1005 $backups = WPStaging::make(ListableBackupsCollection::class)->getListableBackups();
1006
1007 $output = $this->info("Number of Backups:", (string)count($backups));
1008
1009 $totalBackupSize = 0;
1010 foreach ($backups as $backup) {
1011 $totalBackupSize += (float)$backup->size;
1012 }
1013
1014 $output .= $this->info("Total Backups Size:", esc_html((string)size_format($totalBackupSize, 2)));
1015
1016 return $output;
1017 }
1018
1019
1020
1021
1022
1023 protected function constantInfo(string $constantName): string
1024 {
1025 if (!defined($constantName)) {
1026 return $this->info($constantName . ':', self::NOT_SET_LABEL);
1027 }
1028
1029 $constantValue = constant($constantName);
1030 if (is_bool($constantValue)) {
1031 $constantValue = $constantValue ? 'Yes' : 'No';
1032 }
1033
1034 return $this->info($constantName . ':', $constantValue);
1035 }
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045 protected function getSettingValue($settings, string $property, bool $format = false)
1046 {
1047 if (!isset($settings->$property)) {
1048 return self::NOT_SET_LABEL;
1049 }
1050
1051 return $format ? number_format($settings->$property) : $settings->$property;
1052 }
1053
1054
1055
1056
1057
1058
1059
1060 protected function formatBooleanOption($option): string
1061 {
1062 return !empty($option) && $option === 'true' ? 'true' : 'false';
1063 }
1064
1065
1066
1067
1068
1069
1070
1071 protected function sanitizeSitePasswords(array $sites): array
1072 {
1073 foreach ($sites as $key => $clone) {
1074 if (!empty($clone['databasePassword'])) {
1075 $sites[$key]['databasePassword'] = self::REMOVED_LABEL;
1076 }
1077
1078 if (!empty($clone['adminPassword'])) {
1079 $sites[$key]['adminPassword'] = self::REMOVED_LABEL;
1080 }
1081 }
1082
1083 return $sites;
1084 }
1085
1086 private function getPrimaryKeyInfo(): string
1087 {
1088 $tableName = $this->database->getPrefix() . 'options';
1089 $isPrimaryKeyMissing = $this->wpOptionsInfo->isOptionTablePrimaryKeyMissing($tableName);
1090 if ($isPrimaryKeyMissing) {
1091 return $this->info("Primary Key in {$tableName} :", self::NOT_SET_LABEL);
1092 }
1093
1094 $isPrimaryKeyIsOptionName = $this->wpOptionsInfo->isPrimaryKeyIsOptionName($tableName);
1095 if ($isPrimaryKeyIsOptionName) {
1096 return $this->info("Primary Key in {$tableName}:", 'option_name');
1097 }
1098
1099 return $this->info("Primary Key in {$tableName}:", 'option_id');
1100 }
1101
1102 public function setEncodeProLicense(bool $isEncodeProLicense = false)
1103 {
1104 $this->isEncodeProLicense = $isEncodeProLicense;
1105 }
1106
1107 public function getEncodeProLicense(): bool
1108 {
1109 return $this->isEncodeProLicense;
1110 }
1111
1112 private function getLicenseKey()
1113 {
1114 $licenseKey = get_option('wpstg_license_key');
1115 if (empty($licenseKey) || !$this->getEncodeProLicense()) {
1116 return $licenseKey;
1117 }
1118
1119
1120 $dataEncryption = WPStaging::make(DataEncryption::class);
1121
1122 if (!$dataEncryption->isPhpSecLibAvailable()) {
1123 return $licenseKey;
1124 }
1125
1126 $publicKey = $dataEncryption->getPublicKey();
1127 if (empty($publicKey)) {
1128 return $licenseKey;
1129 }
1130
1131 return $dataEncryption->rsaEncrypt($licenseKey, $publicKey);
1132 }
1133
1134
1135
1136
1137
1138
1139 protected function formatStorageSettings(string $optionName, string $title): string
1140 {
1141 $output = PHP_EOL . "-- " . $title . PHP_EOL;
1142
1143 $settings = (array) get_option($optionName, []);
1144 if (!empty($settings)) {
1145
1146 if ($this->enableStructuredOutput) {
1147 $this->info($title, '');
1148 }
1149
1150 foreach ($settings as $key => $value) {
1151 $output .= $this->info($key, empty($value) ? self::NOT_SET_LABEL : $this->removeCredentials($key, $value));
1152 }
1153 }
1154
1155 return $output;
1156 }
1157
1158
1159
1160
1161 protected function muPlugins(): string
1162 {
1163 $this->currentSection = SystemInfoParser::SECTIONS['PLUGINS_OVERVIEW']['id'];
1164 $output = $this->header("Must-Use Plugins");
1165 $muPlugins = get_mu_plugins();
1166 foreach ($muPlugins as $pluginData) {
1167 $output .= $this->info($pluginData["Name"] . ":", $pluginData["Version"]);
1168 }
1169
1170 return $output;
1171 }
1172
1173
1174
1175
1176 protected function dropIns(): string
1177 {
1178 $this->currentSection = SystemInfoParser::SECTIONS['PLUGINS_OVERVIEW']['id'];
1179 $output = $this->header("Drop-Ins");
1180 $dropIns = get_dropins();
1181 foreach ($dropIns as $dropIn) {
1182 $output .= $this->info($dropIn["Name"] . ":", $dropIn["Version"]);
1183 }
1184
1185 return $output;
1186 }
1187 }
1188