PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.9.4
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.9.4
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 / Framework / SiteInfo.php
wp-staging / Framework Last commit date
Adapter 1 week ago Analytics 4 weeks ago Assets 3 weeks ago BackgroundProcessing 1 week ago CloningProcess 4 weeks ago Collection 3 years ago Command 5 years ago Component 9 months ago DI 7 months ago Database 1 month ago DependencyResolver 2 years ago Exceptions 2 years ago Facades 3 months ago Filesystem 1 day ago Interfaces 5 years ago Job 1 day ago Language 3 weeks ago Logger 2 months ago Mails 4 months ago Network 3 weeks ago Newsfeed 5 months ago Notices 3 weeks ago Performance 3 months ago Permalinks 1 year ago Queue 5 months ago Rest 3 months ago Security 2 months ago Settings 2 months ago TemplateEngine 7 months ago ThirdParty 6 months ago Traits 1 day ago Upgrade 2 months ago Utils 1 month ago AnalyticsServiceProvider.php 4 weeks ago AssetServiceProvider.php 1 year ago CommonServiceProvider.php 2 months ago ErrorHandler.php 1 month ago NoticeServiceProvider.php 1 year ago SettingsServiceProvider.php 3 months ago SiteInfo.php 1 day ago Url.php 5 months ago
SiteInfo.php
372 lines
1 <?php
2
3 namespace WPStaging\Framework;
4
5 use WPStaging\Framework\Facades\Hooks;
6 use WPStaging\Framework\Facades\Sanitize;
7 use WPStaging\Staging\CloneOptions;
8
9 /**
10 * Class SiteInfo
11 *
12 * Provides information about the current site.
13 *
14 * @package WPStaging\Site
15 */
16 class SiteInfo
17 {
18 /**
19 * The key used in DB to store is cloneable feature in clone options
20 * @var string
21 */
22 const IS_CLONEABLE_KEY = 'isCloneable';
23
24 /**
25 * The file which make staging site cloneable
26 * This way is depreciated
27 * @var string
28 */
29 const CLONEABLE_FILE = '.wp-staging-cloneable';
30
31 /**
32 * The key used in DB to store whether site is staging or not
33 * @var string
34 */
35 const IS_STAGING_KEY = 'wpstg_is_staging_site';
36
37 /**
38 * The file which makes a site a staging site
39 * @var string
40 */
41 const STAGING_FILE = '.wp-staging';
42
43 /** @var string */
44 const HOSTED_ON_WP = 'wp.com';
45
46 /** @var string */
47 const HOSTED_ON_FLYWHEEL = 'flywheel';
48
49 /** @var string */
50 const HOSTED_ON_BITNAMI = 'bitnami';
51
52 /** @var string */
53 const OTHER_HOST = 'other';
54
55 /** @var string[] */
56 const LOCAL_HOSTNAMES = [
57 '.local',
58 '.test',
59 'localhost',
60 '.dev',
61 '10.0.0.',
62 '172.16.0.',
63 '192.168.0.',
64 ];
65
66 /**
67 * @var CloneOptions
68 */
69 private $cloneOptions;
70
71 /**
72 * @var array
73 */
74 private $errors = [];
75
76 public function __construct()
77 {
78 // TODO: inject using DI
79 $this->cloneOptions = new CloneOptions();
80 }
81
82 /**
83 * @return bool True if it is staging site. False otherwise.
84 */
85 public function isStagingSite(): bool
86 {
87 // Single source of truth lives in the early bootstrap, before the autoloader.
88 return wpstgIsStagingSite(self::STAGING_FILE, self::IS_STAGING_KEY);
89 }
90
91 /**
92 * @return bool True if it is staging site. False otherwise.
93 */
94 public function isCloneable(): bool
95 {
96 // Site should be cloneable if not staging i.e. production site
97 if (!$this->isStagingSite()) {
98 return true;
99 }
100
101 // Old condition to check if staging site is cloneable
102 if (file_exists(ABSPATH . self::CLONEABLE_FILE)) {
103 return true;
104 }
105
106 // New condition for checking whether staging is cloneable or not
107 return $this->cloneOptions->get(self::IS_CLONEABLE_KEY, false);
108 }
109
110 /**
111 * Check if WP is installed in subdirectory
112 * If siteurl and home are not identical we assume the site is located in a subdirectory
113 * related to that instruction https://wordpress.org/support/article/giving-wordpress-its-own-directory/
114 *
115 * @return bool
116 */
117 public function isInstalledInSubDir(): bool
118 {
119 $siteUrl = get_option('siteurl');
120 $homeUrl = get_option('home');
121
122 //Get URL path e.g.https://example.com/path will return /path
123 $siteUrlPath = wp_parse_url($siteUrl, PHP_URL_PATH);
124 $homeUrlPath = wp_parse_url($homeUrl, PHP_URL_PATH);
125
126 if ($siteUrlPath === null && $homeUrlPath === null || $siteUrlPath === $homeUrlPath) {
127 return false;
128 }
129
130 if ($siteUrlPath === null && $homeUrlPath !== null) {
131 return true;
132 }
133
134 return false;
135 }
136
137 /**
138 * Enable the cloning for current staging site.
139 *
140 * @return bool
141 */
142 public function enableStagingSiteCloning(): bool
143 {
144 // Early Bail: if site is not staging
145 if (!$this->isStagingSite()) {
146 return false;
147 }
148
149 // Early Bail: if cloning already enabled
150 if ($this->isCloneable()) {
151 return true;
152 }
153
154 return $this->cloneOptions->set(self::IS_CLONEABLE_KEY, true);
155 }
156
157 /**
158 * Enable the cloning for current staging site.
159 *
160 * @return bool
161 */
162 public function disableStagingSiteCloning(): bool
163 {
164 // Early Bail: if site is not staging
165 if (!$this->isStagingSite()) {
166 return false;
167 }
168
169 // Early Bail: if cloning already disabled
170 if (!$this->isCloneable()) {
171 return true;
172 }
173
174 // First try disabling if cloneable feature exist due to old way.
175 $cloneableFile = trailingslashit(ABSPATH) . self::CLONEABLE_FILE;
176 if (file_exists($cloneableFile) && !unlink($cloneableFile)) {
177 // Error if files exists but unable to unlink
178 return false;
179 }
180
181 // Staging site may have been made cloneable through both ways
182 // So now try disabling through new way
183 return (!file_exists($cloneableFile) && $this->cloneOptions->delete(self::IS_CLONEABLE_KEY));
184 }
185
186 /**
187 * @return bool True if "short_open_tags" is enabled, false if disabled.
188 */
189 public function isPhpShortTagsEnabled(): bool
190 {
191 return in_array(strtolower(ini_get('short_open_tags')), ['1', 'on', 'true']);
192 }
193
194 /**
195 * Is WP Bakery plugin active?
196 *
197 * @return bool
198 */
199 public function isWpBakeryActive(): bool
200 {
201 return defined('WPB_VC_VERSION');
202 }
203
204 /**
205 * Is Jetpack plugin active?
206 *
207 * @return bool
208 */
209 public function isJetpackActive(): bool
210 {
211 return class_exists('Jetpack');
212 }
213
214 /**
215 * @return string[]
216 */
217 public function getErrors(): array
218 {
219 return $this->errors;
220 }
221
222 /**
223 * @return bool
224 */
225 public function isBitnami(): bool
226 {
227 return ABSPATH === '/opt/bitnami/wordpress/';
228 }
229
230 /**
231 * @return bool
232 */
233 public function isWpContentOutsideAbspath(): bool
234 {
235 $wpContentDir = wp_normalize_path(WP_CONTENT_DIR);
236 $abspath = wp_normalize_path(ABSPATH);
237
238 return !(strpos($wpContentDir, $abspath) === 0);
239 }
240
241 /**
242 * @return bool
243 */
244 public function isUploadsOutsideAbspath(): bool
245 {
246 $uploadDir = wp_normalize_path(wp_upload_dir()['basedir']);
247 $abspath = wp_normalize_path(ABSPATH);
248
249 return strpos($uploadDir, $abspath) !== 0;
250 }
251
252 /**
253 * @return bool
254 */
255 public function isFlywheel(): bool
256 {
257 if (!$this->isWpContentOutsideAbspath()) {
258 return false;
259 }
260
261 return file_exists(trailingslashit(wp_normalize_path(ABSPATH)) . '.fw-config.php');
262 }
263
264 /**
265 * @return bool
266 */
267 public function isHostedOnWordPressCom(): bool
268 {
269 if (!$this->isWpContentOutsideAbspath()) {
270 return false;
271 }
272
273 $parentDirectory = dirname(trailingslashit(wp_normalize_path(WP_CONTENT_DIR)));
274 $wpcomDetection = trailingslashit($parentDirectory) . '__wp__';
275 if (!is_link($wpcomDetection)) {
276 return false;
277 }
278
279 return true;
280 }
281
282 /**
283 * @return string
284 */
285 public function getHostingType(): string
286 {
287 if ($this->isFlywheel()) {
288 return self::HOSTED_ON_FLYWHEEL;
289 }
290
291 if ($this->isHostedOnWordPressCom()) {
292 return self::HOSTED_ON_WP;
293 }
294
295 if ($this->isBitnami()) {
296 return self::HOSTED_ON_BITNAMI;
297 }
298
299 return self::OTHER_HOST;
300 }
301
302 public function getPhpArchitecture(): string
303 {
304 return PHP_INT_SIZE === 8 ? '64-bit' : '32-bit';
305 }
306
307 public function getOsArchitecture(): string
308 {
309 try {
310 if (!function_exists('php_uname')) {
311 return 'N/A';
312 }
313
314 if (in_array('php_uname', explode(',', ini_get('disable_functions')))) {
315 return 'N/A';
316 }
317
318 return strpos(php_uname('m'), '64') !== false ? '64-bit' : '32-bit';
319 } catch (\Throwable $ex) {
320 return 'N/A';
321 }
322 }
323
324 /**
325 * @return bool
326 */
327 public function isHostedOnElementorCloud(): bool
328 {
329 $httpHost = !empty($_SERVER['HTTP_HOST']) ? Sanitize::sanitizeString($_SERVER['HTTP_HOST']) : '';
330 if (strpos($httpHost, 'elementor.cloud') !== false) {
331 return true;
332 }
333
334 $headers = headers_list();
335 foreach ($headers as $header) {
336 if (stripos($header, 'ec-source') !== false || stripos($header, 'ec-coldstart') !== false || stripos($header, 'EC-LB-OP-STATUS') !== false) {
337 return true;
338 }
339 }
340
341 return false;
342 }
343
344 /**
345 * Check if the website is installed locally.
346 * @return bool
347 */
348 public function isLocal(): bool
349 {
350 $siteUrl = get_site_url();
351 $isLocal = false;
352
353 foreach (self::LOCAL_HOSTNAMES as $hostname) {
354 if (strpos($siteUrl, $hostname) !== false) {
355 $isLocal = true;
356 break;
357 }
358 }
359
360 return apply_filters('wpstg.tests.is_local_site', $isLocal);
361 }
362
363 /**
364 * Wrapper around is_multisite()
365 * @return bool
366 */
367 public function isMultisite(): bool
368 {
369 return is_multisite();
370 }
371 }
372