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