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