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