PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.16.8
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.16.8
1.19.8 1.19.7 1.19.6 1.19.5 trunk 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.11.0 1.12.0 1.13.0 1.14.0 1.15.0 1.15.1 1.15.2 1.15.3 1.16.0 1.16.1 1.16.2 1.16.3 1.16.4 1.16.5 1.16.6 1.16.7 1.16.8 1.17.0 1.17.6 1.17.7 1.17.8 1.17.9 1.18.0 1.18.1 1.18.2 1.18.3 1.18.4 1.18.5 1.18.6 1.18.7 1.18.8 1.18.9 1.19.0 1.19.1 1.19.2 1.19.3 1.19.4 1.3.19 1.3.20 1.4.0 1.4.1 1.5.0 1.5.1 1.5.10 1.5.11 1.5.12 1.5.13 1.5.14 1.5.15 1.5.16 1.5.17 1.5.18 1.5.19 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.5.9 1.6.0 1.6.1 1.7.0 1.7.1 1.8.0 1.8.1 1.8.3 1.9.0 1.9.1 1.9.2
nitropack / classes / WordPress / NitroPack.php
nitropack / classes / WordPress Last commit date
Config.php 2 years ago NitroPack.php 1 year ago Notifications.php 3 years ago
NitroPack.php
530 lines
1 <?php
2 namespace NitroPack\WordPress;
3
4 use \NitroPack\SDK\Filesystem;
5
6 class NitroPack {
7 private static $instance = NULL;
8 public static $nitroDirMigrated = false;
9 public static $nitroConfigMigrated = false;
10 public static $preUpdatePosts = array();
11 public static $preUpdateTaxonomies = array();
12 public static $preUpdateMeta = array();
13 public static $ignoreUpdatePostIDs = array();
14 public static $np_loggedWarmups = array();
15 public static $optionsToCache = [
16 'cache_handler_cache_handler',
17 'woocommerce_default_customer_address',
18 [ "wc_aelia_currency_switcher" => "ipgeolocation_enabled"]
19 ];
20
21 public static function getInstance() {
22 if (!self::$instance) {
23 self::$instance = new NitroPack();
24 }
25
26 return self::$instance;
27 }
28
29 public static function getDataDir() {
30 $isRaidBoxes = \NitroPack\Integration\Hosting\Raidboxes::detect();
31 $isPantheon = \NitroPack\Integration\Hosting\Pantheon::detect();
32 $isWpe = \NitroPack\Integration\Hosting\WPEngine::detect();
33 $isCloudways = \NitroPack\Integration\Hosting\Cloudways::detect();
34 $isRocketNet = \NitroPack\Integration\Hosting\RocketNet::detect();
35 $currentFilePath = __FILE__;
36 $wpContentDir = WP_CONTENT_DIR;
37 $pathResolved = self::resolvePathNavigation($wpContentDir);
38
39 if ($pathResolved != $wpContentDir) {
40 // Handle existing installations which used to have either /./ or /../ in their content dir paths
41 $wpContentDir = $pathResolved;
42 self::$nitroDirMigrated = true;
43 }
44
45 if ($isWpe) {
46 $currentFilePath = preg_replace("@^/sites/@", "/nas/content/live/", $currentFilePath);
47 $wpContentDir = preg_replace("@^/sites/@", "/nas/content/live/", $wpContentDir);
48 }
49
50 $oldNitroDirs = [
51 nitropack_trailingslashit($wpContentDir) . 'nitropack',
52 nitropack_trailingslashit($wpContentDir) . 'cache/' . substr(md5($currentFilePath), 0, 7) . "-nitropack",
53 ];
54 $newNitroDir = nitropack_trailingslashit($wpContentDir) . 'cache/' . NITROPACK_CACHE_DIR_NAME;
55 $nitroDir = $newNitroDir;
56
57 if ($isRaidBoxes || $isRocketNet || $isCloudways) {
58 $nitroDir = $oldNitroDirs[0];
59 } else if ($isPantheon) {
60 $nitroDir = nitropack_trailingslashit($wpContentDir) . 'uploads/' . NITROPACK_CACHE_DIR_NAME;
61 }
62
63 $possibleDirs = array_unique(array_merge($oldNitroDirs, [$newNitroDir]));
64 $existingDirs = [];
65
66 foreach ($possibleDirs as $possibleDir) {
67 if (Filesystem::fileExists($possibleDir)) {
68 $existingDirs[] = $possibleDir;
69 }
70 }
71
72 if (count($existingDirs) == 1) {
73 $existingDir = array_shift($existingDirs);
74 if (is_link($existingDir)) {
75 $existingDir = readlink($existingDir);
76 }
77
78 if ($existingDir != $nitroDir) {
79 if (!Filesystem::fileExists($nitroDir) && !NITROPACK_USE_REDIS) {
80 // Existing installation, move to the new location
81
82 if (Filesystem::createDir(dirname($nitroDir)) && Filesystem::rename($existingDir, $nitroDir)) {
83 self::$nitroDirMigrated = true;
84 } else {
85 define('NITROPACK_DATA_DIR_WARNING', 'Unable to initialize cache dir because the PHP user does not have permission to create/rename directories under wp-content/. Running in legacy mode. Please contact support for help.');
86 $nitroDir = $existingDir;
87 }
88 }
89 }
90 }
91
92 return $nitroDir;
93 }
94
95 public static function getPluginDataDir() {
96 $isPantheon = \NitroPack\Integration\Hosting\Pantheon::detect();
97 $isWpe = \NitroPack\Integration\Hosting\WPEngine::detect();
98 $nitroDir = nitropack_trailingslashit(WP_CONTENT_DIR) . 'config-' . NITROPACK_CACHE_DIR_NAME;
99 $expectedNitroDir = $nitroDir;
100
101 $nitroDir = self::resolvePathNavigation($nitroDir);
102 if ($nitroDir != $expectedNitroDir) {
103 // Handle existing installations which used to have either /./ or /../ in their config paths
104 $expectedNitroDir = $nitroDir;
105 self::$nitroConfigMigrated = true;
106 }
107
108 if ($isWpe) {
109 $nitroDir = preg_replace("@^/sites/@", "/nas/content/live/", $nitroDir);
110 $expectedNitroDir = $nitroDir;
111 }
112
113 if ($isPantheon) {
114 $nitroDir = nitropack_trailingslashit(WP_CONTENT_DIR) . 'uploads/config-' . NITROPACK_CACHE_DIR_NAME;
115 $expectedNitroDir = $nitroDir;
116 }
117
118 $oldConfigFile = nitropack_trailingslashit(NITROPACK_DATA_DIR) . 'config.json';
119 $newConfigFile = nitropack_trailingslashit($nitroDir) . 'config.json';
120
121 if ($isWpe) {
122 // For WPE it works better if we keep the config file in the cache dir
123 $tmpConfigFile = $newConfigFile;
124 $newConfigFile = $oldConfigFile;
125 $oldConfigFile = $tmpConfigFile;
126
127 if (!Filesystem::fileExists($oldConfigFile) && Filesystem::fileExists(preg_replace("@^/nas/content/live/@", "/sites/", $oldConfigFile))) {
128 $oldConfigFile = preg_replace("@^/nas/content/live/@", "/sites/", $oldConfigFile);
129 }
130
131 $nitroDir = NITROPACK_DATA_DIR;
132 }
133
134 if (Filesystem::fileExists($oldConfigFile) && !Filesystem::fileExists($newConfigFile)) {
135 // Existing installation, move config to the new location
136
137 if (Filesystem::createDir($nitroDir) && Filesystem::rename($oldConfigFile, $newConfigFile)) {
138 self::$nitroConfigMigrated = true;
139 $oldHtaccessFile = nitropack_trailingslashit(NITROPACK_DATA_DIR) . '.htaccess';
140 $newHtaccessFile = nitropack_trailingslashit($nitroDir) . '.htaccess';
141 if (Filesystem::fileExists($oldHtaccessFile) && !Filesystem::fileExists($newHtaccessFile)) {
142 Filesystem::filePutContents($newHtaccessFile, Filesystem::fileGetContents($oldHtaccessFile));
143 }
144
145 if ($isWpe) {
146 Filesystem::deleteDir($expectedNitroDir);
147 }
148 } else {
149 define('NITROPACK_PLUGIN_DATA_DIR_WARNING', 'Unable to initialize plugin data dir because the PHP user does not have permission to create/rename directories under wp-content/. Running in legacy mode. Please contact support for help.');
150 $nitroDir = $isWpe ? $expectedNitroDir : NITROPACK_DATA_DIR;
151 }
152 }
153
154 return $nitroDir;
155 }
156
157 public static function isWpCli() {
158 return defined("WP_CLI") && WP_CLI;
159 }
160
161 private $sdkObjects;
162 private $disabledReason;
163 private $pageType;
164
165 public $Config;
166 public $Notifications;
167
168 public function __construct() {
169 $this->Config = new Config($this);
170 $this->Notifications = new Notifications($this);
171 $this->sdkObjects = array();
172 $this->disabledReason = NULL;
173 $this->pageType = NULL;
174 }
175
176 public function getDistribution() {
177 $dist = "regular";
178 $dbDist = NULL;
179
180 try {
181 if (function_exists("get_option")) {
182 $dbDist = get_option("nitropack-distribution", NULL);
183 }
184
185 if ($this->isConnected()) {
186 $sdk = $this->getSdk();
187 if (!$sdk) return $dbDist ? $dbDist : $dist;
188
189 $config = $sdk->getConfig();
190 if ($config) {
191 $dist = $config->Distribution;
192 }
193 } else if ($dbDist !== NULL) {
194 $dist = $dbDist;
195 }
196
197 if ($dbDist != $dist && function_exists("update_option")) {
198 update_option("nitropack-distribution", $dist);
199 }
200
201 return $dist;
202 } catch (Exception $e) {
203 return $dist;
204 }
205 }
206
207 public function getSiteConfig() {
208 $siteConfig = null;
209 $npConfig = $this->Config->get();
210 $currentUrl = $this -> getCurrentUrl();
211
212 $matchLength = 0;
213
214 foreach ($npConfig as $siteUrl => $config) {
215 if (stripos($siteUrl, "www.") === 0) {
216 $siteUrl = substr($siteUrl, 4);
217 }
218
219 if (stripos($currentUrl, $siteUrl) === 0 && strlen($siteUrl) > $matchLength) {
220 $siteConfig = $config;
221 $matchLength = strlen($siteUrl);
222 }
223 }
224
225 if (!$siteConfig) {
226 $matchLength = 0;
227 foreach ($npConfig as $siteUrl => $config) {
228 if (isset($config['additional_domains'])) {
229 foreach ($config['additional_domains'] as $additionalDomain) {
230 if (stripos($additionalDomain, "www.") === 0) {
231 $additionalDomain = substr($additionalDomain, 4);
232 }
233
234 if (stripos($currentUrl, $additionalDomain) === 0 && strlen($additionalDomain) > $matchLength) {
235 $siteConfig = $config;
236 $matchLength = strlen($additionalDomain);
237 }
238 }
239 }
240 }
241 }
242
243 return $siteConfig;
244 }
245
246 public function getSiteId() {
247 $siteConfig = $this->getSiteConfig();
248 return $siteConfig ? $siteConfig["siteId"] : NULL;
249 }
250
251 public function getSiteSecret() {
252 $siteConfig = $this->getSiteConfig();
253 return $siteConfig ? $siteConfig["siteSecret"] : NULL;
254 }
255
256 /**
257 * Bear in mind that get_home_url() is not defined in the context of advanced_cache.php
258 * so this will throw a fatal error if you call it at that point!
259 */
260 public static function getConfigKey() {
261 return preg_replace("/^https?:\/\/(.*)/", "$1", get_home_url());
262 }
263
264 public function getAdditionalDomains($siteId, $siteSecret) {
265 if (null !== $nitro = $this->getSdk($siteId, $siteSecret)) {
266 $config = $nitro->getConfig();
267 if (!property_exists($config->AdditionalDomains, 'Domains')) {
268 $nitro->fetchConfig();
269 }
270 return $config->AdditionalDomains->Domains;
271 }
272
273 return [];
274 }
275
276 public function isConnected() {
277 return !empty($this->getSiteId()) && !empty($this->getSiteSecret());
278 }
279
280 public function updateCurrentBlogConfig($siteId, $siteSecret, $blogId, $enableCompression = null) {
281 if ($enableCompression === null) {
282 $enableCompression = (get_option('nitropack-enableCompression') == 1);
283 }
284
285 $webhookToken = get_option('nitropack-webhookToken');
286 $hosting = nitropack_detect_hosting();
287
288 $home_url = get_home_url();
289 $admin_url = admin_url();
290 $alwaysBuffer = defined("NITROPACK_ALWAYS_BUFFER") ? NITROPACK_ALWAYS_BUFFER : true;
291 $configKey = self::getConfigKey();
292 $staticConfig = $this->Config->get();
293 $staticConfig[$configKey] = array(
294 "siteId" => $siteId,
295 "siteSecret" => $siteSecret,
296 "blogId" => $blogId,
297 "compression" => $enableCompression,
298 "webhookToken" => $webhookToken,
299 "home_url" => $home_url,
300 "admin_url" => $admin_url,
301 "hosting" => $hosting,
302 "alwaysBuffer" => $alwaysBuffer,
303 "isEzoicActive" => \NitroPack\Integration\Plugin\Ezoic::isActive(),
304 "isApoActive" => \NitroPack\Integration\Plugin\Cloudflare::isApoActive(),
305 "isNginxHelperActive" => \NitroPack\Integration\Plugin\NginxHelper::isActive(),
306 "isLateIntegrationInitRequired" => nitropack_is_late_integration_init_required(),
307 "isDlmActive" => \NitroPack\Integration\Plugin\DownloadManager::isActive(),
308 "isWoocommerceCacheHandlerActive" => \NitroPack\Integration\Plugin\WoocommerceCacheHandler::isActive(),
309 "isWoocommerceActive" => \NitroPack\Integration\Plugin\Woocommerce::isActive(),
310 "isAeliaCurrencySwitcherActive" => \NitroPack\Integration\Plugin\AeliaCurrencySwitcher::isActive(),
311 "isGeoTargetingWPActive" => \NitroPack\Integration\Plugin\GeoTargetingWP::isActive(),
312 "dlm_downloading_url" => \NitroPack\Integration\Plugin\DownloadManager::isActive() ? \NitroPack\Integration\Plugin\DownloadManager::downloadingUrl() : NULL,
313 "dlm_download_endpoint" => \NitroPack\Integration\Plugin\DownloadManager::isActive() ? \NitroPack\Integration\Plugin\DownloadManager::downloadEndpoint() : NULL,
314 "pluginVersion" => NITROPACK_VERSION,
315 "options_cache" => [],
316 "additional_domains" => $this->getAdditionalDomains($siteId, $siteSecret),
317 );
318 foreach (self::$optionsToCache as $opt) {
319 if (is_array($opt)) {
320 foreach($opt as $option => $suboption) {
321 if (empty($staticConfig[$configKey]["options_cache"][$option])) {
322 $staticConfig[$configKey]["options_cache"][$option] = [];
323 }
324 $optionValue = get_option($option);
325 if (!empty($optionValue)) {
326 $staticConfig[$configKey]["options_cache"][$option][$suboption] = $optionValue[$suboption];
327 } else {
328 $staticConfig[$configKey]["options_cache"][$option][$suboption] = null;
329 }
330 }
331 } else {
332 $staticConfig[$configKey]["options_cache"][$opt] = get_option($opt);
333 }
334 }
335 $configSetResult = $this->Config->set($staticConfig);
336
337 if (\NitroPack\Integration\Plugin\AeliaCurrencySwitcher::isActive()) {
338 try {
339 \NitroPack\Integration\Plugin\AeliaCurrencySwitcher::configureVariationCookies();
340 } catch (\Exception $e) {
341 // TODO: Log this error
342 }
343 }
344 if (\NitroPack\Integration\Plugin\GeoTargetingWP::isActive()) {
345 try {
346 \NitroPack\Integration\Plugin\GeoTargetingWP::configureVariationCookies();
347 } catch (\Exception $e) {
348 // TODO: Log this error
349 }
350 }
351
352 return $configSetResult;
353 }
354
355 public function unsetCurrentBlogConfig() {
356 $configKey = self::getConfigKey();
357 $staticConfig = $this->Config->get();
358 if (!empty($staticConfig[$configKey])) {
359 unset($staticConfig[$configKey]);
360 return $this->Config->set($staticConfig);
361 }
362
363 return true;
364 }
365
366 public function resetSdkInstances() {
367 $this->sdkObjects = [];
368 }
369
370 public function getSdk($siteId = null, $siteSecret = null, $urlOverride = NULL, $forwardExceptions = false) {
371 $siteConfig = $this->getSiteConfig();
372
373 $siteId = $siteId ?: (!empty($siteConfig) ? $siteConfig['siteId'] : NULL);
374 $siteSecret = $siteSecret ?: (!empty($siteConfig) ? $siteConfig['siteSecret'] : NULL);
375
376 if ($siteId && $siteSecret) {
377 try {
378 $userAgent = NULL; // It will be automatically detected by the SDK
379 $dataDir = nitropack_trailingslashit(NITROPACK_DATA_DIR) . $siteId; // dir without a trailing slash, because this is how the SDK expects it
380 $cacheKey = "{$siteId}:{$siteSecret}:{$dataDir}";
381
382 if ($urlOverride) {
383 $cacheKey .= ":{$urlOverride}";
384 }
385
386 if (!empty($this->sdkObjects[$cacheKey])) {
387 $nitro = $this->sdkObjects[$cacheKey];
388 } else {
389 if (!defined("NP_COOKIE_FILTER")) {
390 \NitroPack\SDK\NitroPack::addCookieFilter("nitropack_filter_non_original_cookies");
391 define("NP_COOKIE_FILTER", true);
392 do_action('np_set_cookie_filter');
393 }
394 if (!defined("NP_STORAGE_CONFIGURED")) {
395 if (defined("NITROPACK_USE_REDIS") && NITROPACK_USE_REDIS) {
396 \NitroPack\SDK\Filesystem::setStorageDriver(new \NitroPack\SDK\StorageDriver\Redis(
397 NITROPACK_REDIS_HOST,
398 NITROPACK_REDIS_PORT,
399 NITROPACK_REDIS_PASS,
400 NITROPACK_REDIS_DB
401 ));
402 }
403 define("NP_STORAGE_CONFIGURED", true);
404 }
405
406 if (!defined('NP_GEOLOCATION_PREFIX_DEFINED')) {
407 do_action('set_nitropack_geo_cache_prefix');
408 define('NP_GEOLOCATION_PREFIX_DEFINED', true);
409 }
410
411 if (defined("NITROPACK_CUSTOM_CACHE_PREFIX") && !defined('NP_CUSTOM_CACHE_PREFIX_SET')) {
412 \NitroPack\SDK\NitroPack::addCustomCachePrefix(NITROPACK_CUSTOM_CACHE_PREFIX);
413 define('NP_CUSTOM_CACHE_PREFIX_SET', true);
414 }
415
416 $nitro = new \NitroPack\SDK\NitroPack($siteId, $siteSecret, $userAgent, $urlOverride, $dataDir);
417 $this->sdkObjects[$cacheKey] = $nitro;
418 }
419 } catch (\Exception $e) {
420 if ($forwardExceptions) {
421 throw $e;
422 }
423 return NULL;
424 }
425
426 return $nitro;
427 }
428
429 return NULL;
430 }
431
432 public function dataDirExists() {
433 return defined("NITROPACK_DATA_DIR") && is_dir(NITROPACK_DATA_DIR); // TODO: Convert this to use the Filesystem abstraction for better Redis support
434 }
435
436 public function initDataDir() {
437 return $this->dataDirExists() || @mkdir(NITROPACK_DATA_DIR, 0755, true); // TODO: Convert this to use the Filesystem abstraction for better Redis support
438 }
439
440 public function pluginDataDirExists() {
441 return defined("NITROPACK_PLUGIN_DATA_DIR") && is_dir(NITROPACK_PLUGIN_DATA_DIR); // TODO: Convert this to use the Filesystem abstraction for better Redis support
442 }
443
444 public function initPluginDataDir() {
445 return $this->pluginDataDirExists() || @mkdir(NITROPACK_PLUGIN_DATA_DIR, 0755, true); // TODO: Convert this to use the Filesystem abstraction for better Redis support
446 }
447
448 public function setDisabledReason($reason) {
449 $this->disabledReason = $reason;
450 nitropack_header("X-Nitro-Disabled-Reason: $reason");
451 }
452
453 public function getDisabledReason() {
454 return $this->disabledReason;
455 }
456
457 public function setPageType($type) {
458 $this->pageType = $type;
459 }
460
461 public function getPageType() {
462 return $this->pageType;
463 }
464
465 /**
466 * Get current url
467 *
468 * @return string The current url
469 */
470 public function getCurrentUrl() {
471
472 if ( defined('NITROPACK_HOST') ) {
473
474 return NITROPACK_HOST;
475 }
476
477 if (! empty( $_SERVER['HTTP_X_FORWARDED_HOST'] )) {
478 $host = $_SERVER['HTTP_X_FORWARDED_HOST'];
479 } else {
480 $host = !empty($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : "";
481 }
482
483 $uri = !empty($_SERVER["REQUEST_URI"]) ? $_SERVER["REQUEST_URI"] : "";
484 $currentUrl = $host . $uri;
485
486 if (empty($currentUrl) || (defined( 'WP_CLI' ) && WP_CLI && trim($currentUrl) == "localhost")) {
487
488 if (function_exists('get_site_url')) {
489 $host = apply_filters('nitropack_current_host', get_site_url());
490 } elseif (function_exists('get_option')) {
491 $host = apply_filters('nitropack_current_host', get_option('siteurl'));
492 }
493
494 if ($host != '') {
495 $site_url = parse_url($host);
496 if (is_array($site_url) && isset($site_url["host"]) && !empty($site_url["host"])) {
497 $currentUrl = $site_url["host"];
498 }
499 }
500 }
501
502 if (stripos($currentUrl, "www.") === 0) {
503 $currentUrl = substr($currentUrl, 4);
504 }
505
506 return $currentUrl;
507 }
508
509 private static function resolvePathNavigation($path) {
510 if (strpos($path, './') !== false) {
511 $path_parts = explode('/', $path);
512 $final_parts = array();
513
514 foreach($path_parts as $part) {
515 if ($part == ".") {
516 continue;
517 } else if ($part == '..') {
518 array_pop($final_parts);
519 } else {
520 $final_parts[] = $part;
521 }
522 }
523
524 $path = implode('/', $final_parts);
525 }
526
527 return $path;
528 }
529 }
530