PluginProbe ʕ •ᴥ•ʔ
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization / 1.19.0
NitroPack – Performance, Page Speed & Cache Plugin for Core Web Vitals, CDN & Image Optimization v1.19.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 / functions.php
nitropack Last commit date
classes 4 months ago languages 4 months ago nitropack-sdk 5 months ago view 4 months ago advanced-cache.php 1 year ago batcache-compat.php 1 year ago constants.php 4 months ago functions.php 4 months ago helpers.php 1 year ago main.php 4 months ago readme.txt 4 months ago uninstall.php 7 months ago
functions.php
3038 lines
1 <?php
2
3 defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
4
5 use NitroPack\WordPress\Settings\TestMode;
6
7 $np_basePath = dirname( __FILE__ ) . '/';
8 require_once $np_basePath . 'nitropack-sdk/autoload.php';
9 require_once $np_basePath . 'constants.php';
10
11 $np_originalRequestCookies = $_COOKIE;
12 $np_customExpirationTimes = array();
13 $np_queriedObj = NULL;
14 $np_loggedPurges = array();
15 $np_loggedInvalidations = array();
16 $np_integrationSetupEvent = "muplugins_loaded";
17
18 function nitropack_is_logged_in() {
19 $nitro = get_nitropack_sdk();
20 $useAccountOverride = $nitro !== NULL && $nitro->isStatefulCacheSatisfied( "account" );
21 if ( $useAccountOverride ) {
22 return false;
23 }
24
25 //used for previewing the site while logged in
26 if ( ! empty( $_GET['previewmode'] ) ) {
27 return false;
28 }
29
30 $loginCookies = array( defined( 'NITROPACK_LOGGED_IN_COOKIE' ) ? NITROPACK_LOGGED_IN_COOKIE : ( defined( 'LOGGED_IN_COOKIE' ) ? LOGGED_IN_COOKIE : '' ) );
31 foreach ( $loginCookies as $loginCookie ) {
32 if ( ! empty( $_COOKIE[ $loginCookie ] ) ) {
33 $parts = explode( '|', urldecode( $_COOKIE[ $loginCookie ] ) );
34 if ( count( $parts ) < 3 ) {
35 continue; // Invalid cookie
36 }
37
38 return time() <= (int) $parts[1];
39 }
40 }
41 $cookieStr = implode( "|", array_keys( $_COOKIE ) );
42
43 return strpos( $cookieStr, "wordpress_logged_in_" ) !== false;
44 }
45
46 function nitropack_passes_cookie_requirements() {
47 $isUserLoggedIn = nitropack_is_logged_in();
48 $cookieStr = implode( "|", array_keys( $_COOKIE ) );
49 $safeCookie = (
50 ( strpos( $cookieStr, "comment_author" ) === false || ! ! get_nitropack()->setDisabledReason( "comment author" ) )
51 && ( strpos( $cookieStr, "wp-postpass_" ) === false || ! ! get_nitropack()->setDisabledReason( "password protected page" ) )
52 );
53
54 $isItemsInCart = ! empty( $_COOKIE["woocommerce_items_in_cart"] );
55 $useCartOverride = nitropack_is_cart_cache_active();
56
57 if ( $isUserLoggedIn ) {
58 get_nitropack()->setDisabledReason( "logged in" );
59 }
60
61 if ( $isItemsInCart && ! $useCartOverride ) {
62 get_nitropack()->setDisabledReason( "items in cart" );
63 }
64
65 // allow registering filters to "nitropack_passes_cookie_requirements"
66 return apply_filters( "nitropack_passes_cookie_requirements", $safeCookie && ( ! $isItemsInCart || $useCartOverride ) && ( ! $isUserLoggedIn ) );
67 }
68
69 function nitropack_activate() {
70 nitropack_set_wp_cache_const( true );
71
72 $htaccessFile = nitropack_trailingslashit( NITROPACK_DATA_DIR ) . ".htaccess";
73 if ( ! file_exists( $htaccessFile ) && get_nitropack()->initDataDir() ) {
74 file_put_contents( $htaccessFile, "deny from all" );
75 }
76
77 $pluginHtaccessFile = nitropack_trailingslashit( NITROPACK_PLUGIN_DATA_DIR ) . ".htaccess";
78 if ( ! file_exists( $pluginHtaccessFile ) && get_nitropack()->initPluginDataDir() ) {
79 file_put_contents( $pluginHtaccessFile, "deny from all" ); // TODO: Convert this to use the Filesystem abstraction for better Redis support
80 }
81
82 nitropack_install_advanced_cache();
83
84 // Htaccess mods need to happen after installing the advanced cache file so the healthcheck can execute fast
85 nitropack_set_htaccess_rules( true );
86
87 try {
88 do_action( 'nitropack_integration_purge_all' );
89 } catch (\Exception $e) {
90 // Exception while signaling our 3rd party integration addons to purge their cache
91 }
92
93 if ( get_nitropack()->isConnected() ) {
94 nitropack_event( "enable_extension" );
95 // Refresh needed to make sure we have the latest config
96 get_nitropack()->settings->set_required_settings();
97 } else {
98 setcookie( "nitropack_after_activate_notice", 1, time() + 3600 );
99 }
100
101 if ( function_exists( "opcache_reset" ) ) {
102 opcache_reset();
103 }
104 ( new \NitroPack\WordPress\Cron() )->schedule_events();
105
106 // Avoid redirecting when bulk activating plugins
107 if (
108 ( isset( $_REQUEST['action'] ) && 'activate-selected' === $_REQUEST['action'] ) &&
109 ( isset( $_POST['checked'] ) && count( $_POST['checked'] ) > 1 ) ) {
110 return;
111 }
112 add_option( 'nitropack-activation-redirect', wp_get_current_user()->ID );
113
114 }
115
116 function nitropack_activation_redirect() {
117 if ( defined( 'DOING_AJAX' ) || defined( 'WP_CLI' ) ) {
118 return;
119 }
120 global $pagenow;
121 $allowed_pages = [ 'plugins.php', 'plugin-install.php' ];
122 if ( ! in_array( $pagenow, $allowed_pages, true ) ) {
123 return;
124 }
125 // Make sure it's the correct user
126 if ( intval( get_option( 'nitropack-activation-redirect', false ) ) === wp_get_current_user()->ID ) {
127 delete_option( 'nitropack-activation-redirect' );
128 wp_safe_redirect( admin_url( 'admin.php?page=nitropack' ) );
129 exit;
130 }
131 }
132 add_action( 'admin_init', 'nitropack_activation_redirect' );
133
134 function nitropack_deactivate() {
135 nitropack_set_htaccess_rules( false );
136 nitropack_set_wp_cache_const( false );
137 nitropack_uninstall_advanced_cache();
138
139 try {
140 do_action( 'nitropack_integration_purge_all' );
141 } catch (\Exception $e) {
142 // Exception while signaling our 3rd party integration addons to purge their cache
143 }
144
145 if ( get_nitropack()->isConnected() ) {
146 nitropack_event( "disable_extension" );
147 }
148
149 if ( function_exists( "opcache_reset" ) ) {
150 opcache_reset();
151 }
152 // Unscheduling events from the cron.
153 \NitroPack\WordPress\Cron::unschedule_events();
154 }
155
156 function nitropack_install_advanced_cache() {
157 $conflictingPlugins = \NitroPack\WordPress\ConflictingPlugins::getInstance();
158 $nitropack_is_conflicting_plugin_active = $conflictingPlugins->nitropack_is_conflicting_plugin_active();
159 if ( $nitropack_is_conflicting_plugin_active )
160 return false;
161 if ( ! nitropack_is_advanced_cache_allowed() )
162 return false;
163
164 $templatePath = nitropack_trailingslashit( __DIR__ ) . "advanced-cache.php";
165 if ( file_exists( $templatePath ) ) {
166 $contents = file_get_contents( $templatePath );
167 $contents = str_replace( "/*NITROPACK_FUNCTIONS_FILE*/", __FILE__, $contents );
168 $contents = str_replace( "/*NITROPACK_ABSPATH*/", ABSPATH, $contents );
169 $contents = str_replace( "/*LOGIN_COOKIES*/", defined( "LOGGED_IN_COOKIE" ) ? LOGGED_IN_COOKIE : "", $contents );
170 $contents = str_replace( "/*NP_VERSION*/", NITROPACK_VERSION, $contents );
171
172 $advancedCacheFile = nitropack_trailingslashit( WP_CONTENT_DIR ) . 'advanced-cache.php';
173 if ( WP_DEBUG ) {
174 return file_put_contents( $advancedCacheFile, $contents );
175 } else {
176 return @file_put_contents( $advancedCacheFile, $contents );
177 }
178 }
179 }
180
181 function nitropack_uninstall_advanced_cache() {
182 $advancedCacheFile = nitropack_trailingslashit( WP_CONTENT_DIR ) . 'advanced-cache.php';
183 if ( file_exists( $advancedCacheFile ) ) {
184 if ( WP_DEBUG ) {
185 return file_put_contents( $advancedCacheFile, "" );
186 } else {
187 return @file_put_contents( $advancedCacheFile, "" );
188 }
189 }
190 }
191
192 function nitropack_set_wp_cache_const( $status ) {
193 if ( \NitroPack\Integration\Hosting\Flywheel::detect() ) { // Flywheel: This is configured throught the FW control panel
194 return true;
195 }
196
197 if ( \NitroPack\Integration\Hosting\Pressable::detect() ) { // Pressable: We need to deal with Batcache here
198 return nitropack_set_batcache_compat( $status );
199 }
200
201 $configFilePath = nitropack_get_wpconfig_path();
202 if ( ! $configFilePath )
203 return false;
204
205 $newVal = sprintf( "define( 'WP_CACHE', %s /* Modified by NitroPack */ );\n", ( $status ? "true" : "false" ) );
206 $replacementVal = sprintf( " %s /* Modified by NitroPack */ ", ( $status ? "true" : "false" ) );
207 $lines = file( $configFilePath );
208
209 if ( empty( $lines ) )
210 return false;
211
212 $wpCacheFound = false;
213 $phpOpeningTagLine = false;
214
215 foreach ( $lines as $lineIndex => &$line ) {
216 if ( strpos( $line, "<?php" ) !== false && strpos( $line, "?>" ) === false ) {
217 $phpOpeningTagLine = $lineIndex;
218 }
219
220 if ( ! $wpCacheFound && preg_match( "/define\s*\(\s*['\"](.*?)['\"].?,(.*?)\)/", $line, $matches ) ) {
221 if ( $matches[1] == "WP_CACHE" ) {
222 $line = str_replace( $matches[2], $replacementVal, $line );
223 $wpCacheFound = true;
224 }
225 }
226
227 if ( $phpOpeningTagLine !== false && $wpCacheFound !== false )
228 break;
229 }
230
231 if ( ! $wpCacheFound ) {
232 if ( ! $status )
233 return true; // No need to modify the file at all
234
235 if ( $phpOpeningTagLine !== false ) {
236 array_splice( $lines, $phpOpeningTagLine + 1, 0, [ $newVal ] );
237 } else {
238 array_unshift( $lines, "<?php " . trim( $newVal ) . " ?>\n" );
239 }
240 }
241
242 return WP_DEBUG ? file_put_contents( $configFilePath, implode( "", $lines ) ) : @file_put_contents( $configFilePath, implode( "", $lines ) );
243 }
244
245 function nitropack_set_htaccess_rules( $status ) {
246 if ( ! apply_filters( 'nitropack_should_modify_htaccess', false ) )
247 return true;
248
249 $htaccessFilePath = nitropack_get_htaccess_path();
250 if ( ! $htaccessFilePath )
251 return false;
252
253 $htaccessBackupFilePath = $htaccessFilePath . ".nitrobackup";
254 $backupExists = WP_DEBUG ? file_exists( $htaccessBackupFilePath ) : @file_exists( $htaccessBackupFilePath );
255 if ( ! $backupExists ) {
256 $isBackupSuccess = WP_DEBUG ? copy( $htaccessFilePath, $htaccessBackupFilePath ) : @copy( $htaccessFilePath, $htaccessBackupFilePath );
257 if ( ! $isBackupSuccess )
258 return false;
259 }
260
261 $lines = file( $htaccessFilePath );
262 $linesBackup = $lines;
263
264 if ( empty( $lines ) )
265 return false; // We might want to remove this check
266
267 // Remove the old LiteSpeed rules. We need to do this because LiteSpeed's rules are not compatible with NitroPack's rules.
268 if ( $status ) {
269
270 $nitroLsOpenLine = false;
271 $nitroLsCloseLine = false;
272
273 foreach ( $lines as $lineIndex => &$line ) {
274 if ( trim( $line ) == "# BEGIN LSCACHE" ) {
275 $nitroLsOpenLine = $lineIndex;
276 }
277
278 if ( trim( $line ) == "# END LSCACHE" ) {
279 $nitroLsCloseLine = $lineIndex;
280 }
281 }
282
283 if ( $nitroLsOpenLine !== false && $nitroLsCloseLine !== false && $nitroLsCloseLine > $nitroLsOpenLine ) {
284
285 array_splice( $lines, $nitroLsOpenLine, $nitroLsCloseLine - $nitroLsOpenLine + 1 );
286 }
287 }
288
289 $nitroOpenLine = false;
290 $nitroCloseLine = false;
291
292 foreach ( $lines as $lineIndex => &$line ) {
293 if ( trim( $line ) == "# BEGIN NITROPACK" ) {
294 $nitroOpenLine = $lineIndex;
295 }
296
297 if ( trim( $line ) == "# END NITROPACK" ) {
298 $nitroCloseLine = $lineIndex;
299 }
300 }
301
302 $nitroLines = [];
303
304 if ( // We either didn't find the NitroPack markers or we found both in the correct order
305 ( $nitroOpenLine === false && $nitroCloseLine === false ) ||
306 ( $nitroOpenLine !== false && $nitroCloseLine !== false && $nitroCloseLine > $nitroOpenLine )
307 ) {
308 $nitroLines[] = "# BEGIN NITROPACK";
309 if ( $status ) {
310 $rules = apply_filters( "nitropack_htaccess_rules", [] );
311
312 if ( is_string( $rules ) ) {
313 $rules = explode( "\n", $rules );
314 }
315
316 if ( is_array( $rules ) ) {
317 $nitroLines = array_merge( $nitroLines, $rules );
318 }
319 }
320 $nitroLines[] = "# END NITROPACK";
321 $nitroLines = array_map( function ( $line ) {
322 return trim( $line ) . "\n";
323 }, $nitroLines );
324
325 // Begin .htaccess modification
326 $offset = $nitroOpenLine !== false ? $nitroOpenLine : 0;
327 $length = $nitroOpenLine !== false ? $nitroCloseLine - $nitroOpenLine + 1 : 0;
328 array_splice( $lines, $offset, $length, $nitroLines );
329 $writeResult = WP_DEBUG ? file_put_contents( $htaccessFilePath, implode( "", $lines ) ) : @file_put_contents( $htaccessFilePath, implode( "", $lines ) );
330 if ( $writeResult ) {
331 $homeUrl = NULL;
332 $siteConfig = get_nitropack()->getSiteConfig();
333
334 if ( $siteConfig && ! empty( $siteConfig["home_url"] ) ) {
335 $homeUrl = $siteConfig["home_url"];
336 } else if ( function_exists( get_home_url() ) ) {
337 $homeUrl = get_home_url();
338 }
339
340 if ( $homeUrl ) {
341 $homeUrl .= ( strpos( $homeUrl, "?" ) === false ? "?" : "&" ) . "nitroHealthcheck=1";
342 try {
343 $client = new \NitroPack\HttpClient\HttpClient( $homeUrl );
344 $client->timeout = 5;
345 $client->setHeader( "Accept", "text/html" );
346 $client->fetch();
347 if ( $client->getStatusCode() != 200 ) {
348 // Restore the initial version of the file
349 WP_DEBUG ? file_put_contents( $htaccessFilePath, implode( "", $linesBackup ) ) : @file_put_contents( $htaccessFilePath, implode( "", $linesBackup ) );
350 return false;
351 }
352 } catch (\Exception $e) {
353 return false;
354 // Unfortunately we can't be certain whether an issue appeared due to the .htaccess mods
355 // There are no known cases of this happening, so it's fairly safe to assume that all is fine
356 // There are server setups which do not allow loopback requests, which is the more likely reason to end up here
357 // However we can't be certain which one it is, so we are taking the safer approach
358 }
359 } else {
360 return false;
361 }
362 }
363 return $writeResult;
364 }
365
366 return true;
367 }
368
369 function nitropack_set_batcache_compat( $status ) {
370 $currentCompatStatus = defined( "NITROPACK_BATCACHE_COMPAT" ) && NITROPACK_BATCACHE_COMPAT;
371 if ( $currentCompatStatus === $status )
372 return true;
373
374 $configFilePath = nitropack_get_wpconfig_path();
375 if ( ! $configFilePath )
376 return false;
377
378 $batCacheFilePath = NITROPACK_PLUGIN_DIR . "batcache-compat.php";
379 $compatInclude = sprintf( "if (file_exists(\"%s\")) { require_once \"%s\"; } // NitroPack compatibility with Batcache\n", $batCacheFilePath, $batCacheFilePath );
380 $lines = file( $configFilePath );
381
382 if ( empty( $lines ) )
383 return false;
384
385 foreach ( $lines as $lineIndex => &$line ) {
386 if ( preg_match( "/nitropack.*?batcache/i", $line ) ) {
387 $line = "//REMOVE AT FILTER";
388 }
389 }
390
391 $newLines = array_filter( $lines, function ( $line ) {
392 return $line != "//REMOVE AT FILTER";
393 } );
394
395 if ( $status ) {
396 $phpOpeningTagLine = false;
397
398 unset( $line );
399 foreach ( $newLines as $lineIndex => $line ) {
400 if ( strpos( $line, "<?php" ) !== false && strpos( $line, "?>" ) === false ) {
401 $phpOpeningTagLine = $lineIndex;
402 break;
403 }
404 }
405
406 if ( $phpOpeningTagLine !== false ) {
407 array_splice( $newLines, $phpOpeningTagLine + 1, 0, [ $compatInclude ] );
408 } else {
409 array_unshift( $newLines, "<?php " . trim( $compatInclude ) . " ?>\n" );
410 }
411 }
412
413 return WP_DEBUG ? file_put_contents( $configFilePath, implode( "", $newLines ) ) : @file_put_contents( $configFilePath, implode( "", $newLines ) );
414 }
415
416 function is_valid_nitropack_webhook() {
417 return ! empty( $_GET["nitroWebhook"] ) && ! empty( $_GET["token"] ) && nitropack_validate_webhook_token( $_GET["token"] );
418 }
419
420 function is_valid_nitropack_beacon() {
421 if ( ! isset( $_POST["nitroBeaconUrl"] ) || ! isset( $_POST["nitroBeaconHash"] ) )
422 return false;
423
424 $siteConfig = nitropack_get_site_config();
425 if ( ! $siteConfig || empty( $siteConfig["siteSecret"] ) )
426 return false;
427
428
429 if ( function_exists( "hash_hmac" ) && function_exists( "hash_equals" ) ) {
430 $url = base64_decode( $_POST["nitroBeaconUrl"] );
431 $cookiesJson = ! empty( $_POST["nitroBeaconCookies"] ) ? base64_decode( $_POST["nitroBeaconCookies"] ) : ""; // We need to fall back to empty string to remain backwards compatible. Otherwise cache files invalidated before an upgrade will never get updated :(
432 $layout = ! empty( $_POST["layout"] ) ? $_POST["layout"] : "";
433 $localHash = hash_hmac( "sha512", $url . $cookiesJson . $layout, $siteConfig["siteSecret"] );
434 return hash_equals( $_POST["nitroBeaconHash"], $localHash );
435 } else {
436 return ! empty( $_POST["nitroBeaconUrl"] );
437 }
438 }
439
440 function nitropack_handle_beacon() {
441 global $np_originalRequestCookies;
442 if ( ! defined( "NITROPACK_BEACON_HANDLED" ) ) {
443 define( "NITROPACK_BEACON_HANDLED", 1 );
444 } else {
445 return;
446 }
447
448 $siteConfig = nitropack_get_site_config();
449 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) && ! empty( $siteConfig["siteSecret"] ) && ! empty( $_POST["nitroBeaconUrl"] ) ) {
450 $url = base64_decode( $_POST["nitroBeaconUrl"] );
451
452 if ( ! empty( $_POST["nitroBeaconCookies"] ) ) {
453 $np_originalRequestCookies = json_decode( base64_decode( $_POST["nitroBeaconCookies"] ), true );
454 }
455
456 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Beacon request received for URL: ' . $url );
457
458 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"], $url ) ) {
459 try {
460
461 $hasLocalCache = $nitro->hasLocalCache( false );
462 $needsHeartbeat = nitropack_is_heartbeat_needed();
463 $proxyPurgeOnly = ! empty( $_POST["proxyPurgeOnly"] );
464 $layout = ! empty( $_POST["layout"] ) ? $_POST["layout"] : "default";
465 $output = "";
466
467 if ( ! $proxyPurgeOnly ) {
468 if ( ! $hasLocalCache ) {
469 nitropack_header( "X-Nitro-Beacon: FORWARD" );
470 try {
471 $hasCache = $nitro->hasRemoteCache( $layout, false ); // Download the new cache file
472 $hasLocalCache = $hasCache;
473 $output = sprintf( "Cache %s", $hasCache ? "fetched" : "requested" );
474 } catch (\Exception $e) {
475 // not a critical error, do nothing
476 }
477 } else {
478 nitropack_header( "X-Nitro-Beacon: SKIP" );
479 $output = sprintf( "Cache exists already" );
480 }
481 }
482
483 if ( $hasLocalCache || $proxyPurgeOnly/* || $needsHeartbeat*/ ) { // proxyPurgeOnly is set for unsupported browsers, in which case we need to purge the cache regardless of the existence of local NP cache
484 nitropack_header( "X-Nitro-Proxy-Purge: true" );
485 $nitro->purgeProxyCache( $url );
486 do_action( 'nitropack_integration_purge_url', $url );
487 }
488
489 \NitroPack\ModuleHandler::onShutdown( function () use ($output) {
490 echo $output;
491 } );
492 } catch (Exception $e) {
493 // not a critical error, do nothing
494 }
495 }
496 }
497 \NitroPack\ModuleHandler::onCriticalInit( function () {
498 exit;
499 } );
500 }
501
502 /**
503 * Handle NitroPack webhooks
504 *
505 * @return void
506 */
507 /**
508 * Handles the NitroPack webhook request
509 *
510 * @return void
511 */
512 function nitropack_handle_webhook() {
513 if ( defined( 'NITROPACK_DEBUG_MODE' ) ) {
514 do_action( 'nitropack_debug_webhook', $_REQUEST );
515 }
516 if ( ! defined( "NITROPACK_WEBHOOK_HANDLED" ) ) {
517 define( "NITROPACK_WEBHOOK_HANDLED", 1 );
518 } else {
519 return;
520 }
521
522 $siteConfig = nitropack_get_site_config();
523 if ( $siteConfig && $siteConfig["webhookToken"] == $_GET["token"] ) {
524 switch ( $_GET["nitroWebhook"] ) {
525 case "config":
526 nitropack_fetch_config();
527 get_nitropack()->resetSdkInstances(); // This is needed in order to obtain a new SDK instance with the fresh config
528 nitropack_set_htaccess_rules( true );
529 if ( null !== $nitro = get_nitropack_sdk() ) {
530 $nitro->purgeProxyCache();
531 }
532 do_action( 'nitropack_integration_purge_all' );
533 break;
534 case "cache_ready":
535 if ( isset( $_POST['url'] ) ) {
536 $urls = array( $_POST['url'] );
537 } elseif ( isset( $_POST['urls'] ) ) {
538 $urls = $_POST['urls'];
539 } else {
540 $urls = array();
541 }
542 if ( ! empty( $urls ) ) {
543 $readyUrls = [];
544 foreach ( $urls as $url ) {
545 $readyUrl = nitropack_sanitize_url_input( $url );
546 if ( $readyUrl ) {
547 $readyUrls[] = $readyUrl;
548 }
549 }
550
551 if ( $readyUrls && null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"], $readyUrls[0] ) ) {
552 $hasCache = $nitro->hasRemoteCacheMulti( $readyUrls, "default", false ); // Download the new cache file
553 foreach ( $readyUrls as $readyUrl ) {
554 $nitro->purgeProxyCache( $readyUrl );
555 do_action( 'nitropack_integration_purge_url', $readyUrl );
556 }
557 }
558 }
559 break;
560 case "cache_clear":
561 if ( isset( $_POST['url'] ) ) {
562 $urls = array( $_POST['url'] );
563 } elseif ( isset( $_POST['urls'] ) ) {
564 $urls = $_POST['urls'];
565 }
566
567 $proxyPurgeOnly = ! empty( $_POST["proxyPurgeOnly"] );
568 $doAction = ! empty( $_POST['useInvalidate'] )
569 ? static function ( $url = null ) {
570 nitropack_sdk_invalidate_local( $url );
571 }
572 : static function ( $url = null ) {
573 nitropack_sdk_purge_local( $url );
574 };
575
576 if ( ! empty( $_POST["url"] ) ) {
577 $urls = is_array( $_POST["url"] ) ? $_POST["url"] : array( $_POST["url"] );
578 foreach ( $urls as $url ) {
579 $sanitizedUrl = nitropack_sanitize_url_input( $url );
580 if ( $proxyPurgeOnly ) {
581 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
582 $nitro->purgeProxyCache( $sanitizedUrl );
583 }
584 do_action( 'nitropack_integration_purge_url', $sanitizedUrl );
585 } else {
586 $doAction( $sanitizedUrl );
587 }
588 }
589 } else {
590 if ( $proxyPurgeOnly ) {
591 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
592 $nitro->purgeProxyCache();
593 }
594 do_action( 'nitropack_integration_purge_all' );
595 } else {
596 $doAction();
597 nitropack_sdk_delete_backlog();
598 }
599 }
600 break;
601 }
602 }
603 \NitroPack\ModuleHandler::onCriticalInit( function () {
604 nitropack_json_and_exit( array(
605 "type" => "success",
606 ) );
607 } );
608 }
609
610 function nitropack_sanitize_url_input( $url ) {
611 $result = NULL;
612 if ( ! function_exists( "esc_url" ) ) {
613 $sanitizedUrl = filter_var( $url, FILTER_SANITIZE_URL );
614 if ( $sanitizedUrl !== false && filter_var( $sanitizedUrl, FILTER_VALIDATE_URL ) !== false ) {
615 $result = $sanitizedUrl;
616 }
617 } else if ( $validatedUrl = esc_url( $url, array( "http", "https" ), "notdisplay" ) ) {
618 $result = $validatedUrl;
619 }
620
621 return $result;
622 }
623
624 function nitropack_is_amp_page() {
625 return ( function_exists( 'amp_is_request' ) && amp_is_request() && ! get_nitropack()->setDisabledReason( "amp page" ) ) ||
626 ( function_exists( 'ampforwp_is_amp_endpoint' ) && ampforwp_is_amp_endpoint() && ! get_nitropack()->setDisabledReason( "amp page" ) );
627 }
628
629 function nitropack_passes_page_requirements( $detectIfNoCachedResult = true ) {
630 static $cachedResult = NULL;
631 $reduceCheckoutChecks = defined( "NITROPACK_REDUCE_CHECKOUT_CHECKS" ) && NITROPACK_REDUCE_CHECKOUT_CHECKS;
632 $reduceCartChecks = defined( "NITROPACK_REDUCE_CART_CHECKS" ) && NITROPACK_REDUCE_CART_CHECKS;
633
634 if ( $cachedResult === NULL && $detectIfNoCachedResult ) {
635
636 $cachedResult = ! (
637 ( is_404() && ! get_nitropack()->setDisabledReason( "404" ) ) ||
638 ( is_preview() && ! get_nitropack()->setDisabledReason( "preview page" ) ) ||
639 ( is_feed() && ! get_nitropack()->setDisabledReason( "feed" ) ) ||
640 ( is_comment_feed() && ! get_nitropack()->setDisabledReason( "comment feed" ) ) ||
641 ( is_trackback() && ! get_nitropack()->setDisabledReason( "trackback" ) ) ||
642 ( nitropack_is_logged_in() && ! get_nitropack()->setDisabledReason( "logged in" ) ) ||
643 ( is_search() && ! get_nitropack()->setDisabledReason( "search" ) ) ||
644 ( nitropack_is_ajax() && ! get_nitropack()->setDisabledReason( "ajax" ) ) ||
645 ( nitropack_is_post() && ! get_nitropack()->setDisabledReason( "post request" ) ) ||
646 ( nitropack_is_xmlrpc() && ! get_nitropack()->setDisabledReason( "xmlrpc" ) ) ||
647 ( nitropack_is_robots() && ! get_nitropack()->setDisabledReason( "robots" ) ) ||
648 nitropack_is_amp_page() ||
649 ! nitropack_is_allowed_request() ||
650 ( nitropack_is_wp_cron() && ! get_nitropack()->setDisabledReason( "doing cron" ) ) || // CRON request
651 ( nitropack_is_wp_cli() ) || // CLI request
652 ( defined( 'WC_PLUGIN_FILE' ) && ( is_page( 'cart' ) || ( ! $reduceCartChecks && is_cart() ) ) && ! get_nitropack()->setDisabledReason( "cart page" ) ) || // WooCommerce
653 ( defined( 'WC_PLUGIN_FILE' ) && ( is_page( 'checkout' ) || ( ! $reduceCheckoutChecks && is_checkout() ) ) && ! get_nitropack()->setDisabledReason( "checkout page" ) ) || // WooCommerce
654 ( defined( 'WC_PLUGIN_FILE' ) && is_account_page() && ! get_nitropack()->setDisabledReason( "account page" ) ) // WooCommerce
655 );
656 }
657
658 return $cachedResult;
659 }
660
661 function nitropack_is_home() {
662 if ( 'posts' == get_option( 'show_on_front' ) ) {
663 return is_front_page() || is_home();
664 } else {
665 return is_front_page();
666 }
667 }
668
669 function nitropack_is_blogindex() {
670 return is_home();
671 }
672
673 function nitropack_is_archive() {
674 return apply_filters( "nitropack_is_archive_page", is_author() || is_archive() );
675 }
676
677 /* We have a lot of requests with these GET parameters that we want to ignore */
678 function nitropack_get_ignored_get_params() {
679 $urls = [ 'shop_order', 'shop_order_refund', 'revisionPreview' ];
680 return $urls;
681 }
682 function nitropack_is_allowed_request() {
683 global $np_queriedObj;
684 $CPTOptimization = NitroPack\WordPress\Settings\CPTOptimization::getInstance();
685 $cacheableObjectTypes = $CPTOptimization->nitropack_get_cacheable_object_types();
686 if ( is_array( $cacheableObjectTypes ) ) {
687 if ( nitropack_is_home() ) {
688 if ( ! in_array( 'home', $cacheableObjectTypes ) ) {
689 get_nitropack()->setDisabledReason( "page type not allowed (home)" );
690 return false;
691 }
692 } else {
693 if ( is_tax() || is_category() || is_tag() ) {
694 $np_queriedObj = get_queried_object();
695 if ( ! empty( $np_queriedObj ) && ! in_array( $np_queriedObj->taxonomy, $cacheableObjectTypes ) ) {
696 get_nitropack()->setDisabledReason( "page type not allowed ({$np_queriedObj->taxonomy})" );
697 return false;
698 }
699 } else {
700 if ( nitropack_is_archive() ) {
701 if ( ! in_array( 'archive', $cacheableObjectTypes ) ) {
702 get_nitropack()->setDisabledReason( "page type not allowed (archive)" );
703 return false;
704 }
705 } else {
706 $postType = get_post_type();
707 if ( ! empty( $postType ) && ! in_array( $postType, $cacheableObjectTypes ) ) {
708 get_nitropack()->setDisabledReason( "page type not allowed ($postType)" );
709 return false;
710 }
711 }
712 }
713 }
714 }
715
716 $ignoredGETParams = nitropack_get_ignored_get_params();
717 foreach ( $ignoredGETParams as $ignoredGETParam ) {
718 if ( ! empty( $_GET[ $ignoredGETParam ] ) ) {
719 get_nitropack()->setDisabledReason( "ignored url parameter detected ($ignoredGETParam)" );
720 return false;
721 }
722 }
723 //add test mode as disabled reason but not when the testnitro parameter is set
724 if ( empty( $_GET['testnitro'] ) && TestMode::getInstance()->is_test_mode_enabled() ) {
725 get_nitropack()->setDisabledReason( "Test Mode" );
726 return false;
727 }
728
729 if ( null !== $nitro = get_nitropack_sdk() ) {
730 return ( $nitro->isAllowedUrl( $nitro->getUrl() ) || get_nitropack()->setDisabledReason( "url not allowed" ) ) &&
731 ( $nitro->isAllowedRequest( true ) || get_nitropack()->setDisabledReason( "request type not allowed" ) );
732 }
733
734 get_nitropack()->setDisabledReason( "site not connected" );
735 return false;
736 }
737
738 function nitropack_is_ajax() {
739 return ( function_exists( "wp_doing_ajax" ) && wp_doing_ajax() ) ||
740 ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ||
741 ( ! empty( $_SERVER["HTTP_X_REQUESTED_WITH"] ) && $_SERVER["HTTP_X_REQUESTED_WITH"] == "XMLHttpRequest" ) ||
742 ( ! empty( $_SERVER["REQUEST_URI"] ) && basename( $_SERVER["REQUEST_URI"] ) == "admin-ajax.php" ) ||
743 ! empty( $_GET["wc-ajax"] );
744 }
745
746 /**
747 * Checking if the current request is wp-cli request
748 *
749 * @return bool
750 */
751 function nitropack_is_wp_cli() {
752 return NitroPack\WordPress\NitroPack::isWpCli();
753 }
754
755 function nitropack_is_wp_cron() {
756 return defined( 'DOING_CRON' ) && DOING_CRON;
757 }
758
759 function nitropack_is_rest() {
760 // Source: https://wordpress.stackexchange.com/a/317041
761 $prefix = rest_get_url_prefix();
762 if (
763 defined( 'REST_REQUEST' ) && REST_REQUEST // (#1)
764 || isset( $_GET['rest_route'] ) // (#2)
765 && strpos( trim( $_GET['rest_route'], '\\/' ), $prefix, 0 ) === 0
766 )
767 return true;
768 // (#3)
769 global $wp_rewrite;
770 if ( $wp_rewrite === null )
771 $wp_rewrite = new WP_Rewrite();
772
773 // (#4)
774 $rest_url = wp_parse_url( trailingslashit( rest_url() ) );
775 $current_url = wp_parse_url( add_query_arg( array() ) );
776 return strpos( $current_url['path'], $rest_url['path'], 0 ) === 0;
777 }
778
779 function nitropack_is_post() {
780 return ( ! empty( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'POST' ) || ( empty( $_SERVER['REQUEST_METHOD'] ) && ! empty( $_POST ) );
781 }
782
783 function nitropack_is_xmlrpc() {
784 return defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST;
785 }
786
787 function nitropack_is_robots() {
788 return is_robots() || ( ! empty( $_SERVER["REQUEST_URI"] ) && basename( parse_url( $_SERVER["REQUEST_URI"], PHP_URL_PATH ) ) === "robots.txt" );
789 }
790
791 // IMPORTANT: This function should only be trusted if NitroPack is connected. Otherwise we may not have information about the admin URL in the config file and it may return an incorrect result
792 function nitropack_is_admin() {
793 if ( ( nitropack_is_ajax() || nitropack_is_rest() ) && ! empty( $_SERVER["HTTP_REFERER"] ) ) {
794 $adminUrl = NULL;
795 $siteConfig = nitropack_get_site_config();
796 if ( $siteConfig && ! empty( $siteConfig["admin_url"] ) ) {
797 $adminUrl = $siteConfig["admin_url"];
798 } else if ( function_exists( "admin_url" ) ) {
799 $adminUrl = admin_url();
800 } else {
801 return is_admin();
802 }
803
804 return strpos( $_SERVER["HTTP_REFERER"], $adminUrl ) === 0;
805 } else {
806 return is_admin();
807 }
808 }
809
810 function nitropack_is_warmup_request() {
811 return ! empty( $_SERVER["HTTP_X_NITRO_WARMUP"] );
812 }
813
814 function nitropack_is_lighthouse_request() {
815 return ! empty( $_SERVER["HTTP_USER_AGENT"] ) && stripos( $_SERVER["HTTP_USER_AGENT"], "lighthouse" ) !== false;
816 }
817
818 function nitropack_is_gtmetrix_request() {
819 return ! empty( $_SERVER["HTTP_USER_AGENT"] ) && stripos( $_SERVER["HTTP_USER_AGENT"], "gtmetrix" ) !== false;
820 }
821
822 function nitropack_is_pingdom_request() {
823 return ! empty( $_SERVER["HTTP_USER_AGENT"] ) && stripos( $_SERVER["HTTP_USER_AGENT"], "pingdom" ) !== false;
824 }
825
826 function nitropack_is_optimizer_request() {
827 return isset( $_SERVER["HTTP_X_NITROPACK_REQUEST"] );
828 }
829
830 function nitropack_init() {
831 global $np_queriedObj;
832 nitropack_header( 'X-Nitro-Cache: MISS' );
833 $GLOBALS["NitroPack.tags"] = array();
834
835 if ( is_valid_nitropack_webhook() ) {
836 nitropack_handle_webhook();
837 } else {
838 if ( is_valid_nitropack_beacon() ) {
839 nitropack_handle_beacon();
840 } else {
841 /* The following if statement should stay as it is written.
842 * is_archive() can return true if visiting a tax, category or tag page, so is_acrchive must be checked last
843 */
844 if ( is_tax() || is_category() || is_tag() ) {
845 $np_queriedObj = get_queried_object();
846 get_nitropack()->setPageType( $np_queriedObj->taxonomy );
847 } else {
848 $layout = nitropack_get_layout();
849 get_nitropack()->setPageType( $layout );
850 }
851
852 add_action( 'wp_footer', 'nitropack_print_element_override', 9999999 );
853 if ( ! isset( $_GET["wpf_action"] ) && nitropack_passes_cookie_requirements() && nitropack_passes_page_requirements() ) {
854 add_action( 'wp_footer', 'nitropack_print_beacon_script' );
855 add_action( 'get_footer', 'nitropack_print_beacon_script' );
856
857 if ( nitropack_is_optimizer_request() ) { // Only care about tags for requests coming from our service. There is no need to do an API request when handling a standard client request.
858 if ( defined( 'FUSION_BUILDER_VERSION' ) ) {
859 add_filter( 'do_shortcode_tag', 'nitropack_handle_fusion_builder_conatainer_expiration', 10, 3 );
860 add_action( 'wp_footer', 'nitropack_set_custom_expiration' );
861 } else {
862 nitropack_set_custom_expiration();
863 }
864
865 $GLOBALS["NitroPack.tags"][ "pageType:" . get_nitropack()->getPageType()] = 1;
866
867 /* The following if statement should stay as it is written.
868 * is_archive() can return true if visiting a tax, category or tag page, so is_acrchive must be checked last
869 */
870 if ( is_tax() || is_category() || is_tag() ) {
871 $np_queriedObj = get_queried_object();
872 $GLOBALS["NitroPack.tags"][ "tax:" . $np_queriedObj->term_taxonomy_id ] = 1;
873 } else {
874 if ( is_single() || is_page() || is_attachment() ) {
875 $singlePost = get_post();
876 if ( $singlePost ) {
877 $GLOBALS["NitroPack.tags"][ "single:" . $singlePost->ID ] = 1;
878 }
879 }
880 }
881
882 // Uncomment the code below in case object cache interferes with correct URL taggig
883 // The code below will attempt to temporarily disable using the object cache only for the requests coming from NitroPack
884 //wp_using_ext_object_cache(false);
885 //add_action("pre_get_posts", function($query) {
886 // $query->query_vars["cache_results"] = false;
887 //});
888 //
889 //add_filter("all", function() {
890 // $args = func_get_args();
891 // if (count($args) > 1) {
892 // list($filterName, $value) = func_get_args();
893 // if (preg_match("/^transient_(.*)/", $filterName, $matches) && $value) {
894 // return false;
895 // }
896 // }
897 //}, 10, 2);
898
899 add_filter( 'post_link', 'nitropack_post_link_listener', 10, 3 );
900 add_action( 'the_post', 'nitropack_handle_the_post' );
901 add_action( 'wp_footer', 'nitropack_log_tags' );
902 }
903 } else {
904 nitropack_header( "X-Nitro-Disabled: 1" );
905 if ( ( null !== $nitro = get_nitropack_sdk() ) && ! $nitro->isAllowedBrowser() ) { // This clears any proxy cache when a proxy cached non-optimized request due to unsupported browser
906 add_action( 'wp_footer', 'nitropack_print_beacon_script' );
907 add_action( 'get_footer', 'nitropack_print_beacon_script' );
908 }
909 }
910
911 if ( ! nitropack_is_optimizer_request() ) {
912 add_action( 'wp_head', 'nitropack_print_generic_nitro_script' );
913 }
914 }
915 }
916 }
917
918 function nitropack_handle_fusion_builder_conatainer_expiration( $output, $tag, $attr ) {
919 global $np_customExpirationTimes;
920 if ( $tag == "fusion_builder_container" ) {
921 if ( ! empty( $attr["publish_date"] ) && ! empty( $attr["status"] ) && in_array( $attr["status"], array( "published_until", "publish_after" ) ) ) {
922 $timezone = get_option( 'timezone_string' );
923 $offset = get_option( 'gmt_offset' );
924 $dt = new DateTime( $attr["publish_date"] );
925 if ( $timezone ) {
926 $timeZone = new DateTimeZone( $timezone );
927 $timeZoneOffset = $timeZone->getOffset( $dt );
928 } else if ( $offset ) {
929 $timeZoneOffset = (int) $offset * 3600;
930 }
931 $time = $dt->getTimestamp() - $timeZoneOffset;
932 if ( $time > time() ) { // We only need to look at future dates
933 $np_customExpirationTimes[] = $time;
934 }
935 }
936 }
937 return $output;
938 }
939 function nitropack_set_custom_expiration() {
940
941 //check which CPTs are marked for optimization
942 $CPTOptimization = NitroPack\WordPress\Settings\CPTOptimization::getInstance();
943 $get_optimized_CPTs = $CPTOptimization->nitropack_get_optimized_CPTs();
944 if ( empty( $get_optimized_CPTs ) )
945 return;
946
947 global $np_customExpirationTimes, $wpdb;
948
949 $placeholders = implode( ',', array_fill( 0, count( $get_optimized_CPTs ), '%s' ) );
950 $currentDate = date( "Y-m-d H:i:s" );
951
952 //For better security, we use prepared statements
953 $unmodifiedPosts_query = $wpdb->prepare(
954 "SELECT ID, post_date
955 FROM {$wpdb->prefix}posts
956 WHERE {$wpdb->prefix}posts.post_date > %s
957 AND {$wpdb->prefix}posts.post_type IN ($placeholders)
958 AND ({$wpdb->prefix}posts.post_status = 'future')
959 ORDER BY {$wpdb->prefix}posts.post_date ASC
960 LIMIT 0, 1",
961 array_merge( [ $currentDate ], $get_optimized_CPTs )
962 );
963 $unmodifiedPosts = $wpdb->get_results( $unmodifiedPosts_query );
964
965 if ( ! empty( $unmodifiedPosts ) && strtotime( $unmodifiedPosts[0]->post_date ) > time() ) {
966 $scheduled_post = get_post( $unmodifiedPosts[0]->ID );
967
968 // We will check relatedness only if a proper option is set in wp-config.php
969 $check_relatedness = defined( "NITROPACK_SCHEDULED_POST_EXPIRATION_CHECK_RELATEDNESS" ) && NITROPACK_SCHEDULED_POST_EXPIRATION_CHECK_RELATEDNESS;
970
971 // We'll add the expiration time only if the current page is related to the scheduled post or if we don't need to check relatedness
972 if ( ! $check_relatedness || nitropack_is_page_related_to_scheduled_post( $scheduled_post ) ) {
973 $np_customExpirationTimes[] = strtotime( $unmodifiedPosts[0]->post_date );
974 }
975 }
976
977 if ( ! empty( $np_customExpirationTimes ) ) {
978 sort( $np_customExpirationTimes, SORT_NUMERIC );
979 nitropack_header( "X-Nitro-Expires: " . $np_customExpirationTimes[0] );
980 }
981 }
982
983 // Determine if the current page is related to the scheduled post
984 // This is a best-effort approach and may not cover all cases
985 function nitropack_is_page_related_to_scheduled_post( $scheduled_post ) {
986 $current_layout = nitropack_get_layout();
987 $postType = $scheduled_post->post_type;
988 $CPTOptimization = NitroPack\WordPress\Settings\CPTOptimization::getInstance();
989 $cacheableObjectTypes = $CPTOptimization->nitropack_get_cacheable_object_types();
990
991 // Only proceed if post type is cacheable
992 if ( ! in_array( $postType, $cacheableObjectTypes ) ) {
993 return false;
994 }
995
996 // Check if current home page would be invalidated
997 if ( $current_layout === 'home' ) {
998 return true;
999 }
1000
1001 // Check if current blog index page would be invalidated
1002 if ( $postType === 'post' && $current_layout === 'blogindex' ) {
1003 return true;
1004 }
1005
1006 if ( is_post_type_archive( $postType ) ) {
1007 return true;
1008 }
1009
1010 // Check if current single post/page would be invalidated
1011 if ( is_singular( $postType ) ) {
1012 return true;
1013 }
1014
1015 // Default to false for other page types
1016 return false;
1017 }
1018
1019 function nitropack_print_beacon_script() {
1020 if ( defined( "NITROPACK_BEACON_PRINTED" ) || ! nitropack_passes_page_requirements() )
1021 return;
1022 define( "NITROPACK_BEACON_PRINTED", true );
1023 echo apply_filters( "nitro_script_output", nitropack_get_beacon_script() );
1024 }
1025
1026 function nitropack_get_beacon_script() {
1027 $siteConfig = nitropack_get_site_config();
1028 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) && ! empty( $siteConfig["siteSecret"] ) ) {
1029 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
1030 $url = $nitro->getUrl();
1031 $cookiesJson = json_encode( $nitro->supportedCookiesFilter( NitroPack\SDK\NitroPack::getCookies() ) );
1032 $layout = nitropack_get_layout();
1033
1034 if ( function_exists( "hash_hmac" ) && function_exists( "hash_equals" ) ) {
1035 $hash = hash_hmac( "sha512", $url . $cookiesJson . $layout, $siteConfig["siteSecret"] );
1036 } else {
1037 $hash = "";
1038 }
1039 $url = base64_encode( $url ); // We want only ASCII
1040 $cookiesb64 = base64_encode( $cookiesJson );
1041 $proxyPurgeOnly = ! $nitro->isAllowedBrowser();
1042
1043 return "
1044 <script nitro-exclude>
1045 if (!window.NITROPACK_STATE || window.NITROPACK_STATE != 'FRESH') {
1046 var proxyPurgeOnly = " . ( $proxyPurgeOnly ? 1 : 0 ) . ";
1047 if (typeof navigator.sendBeacon !== 'undefined') {
1048 var nitroData = new FormData(); nitroData.append('nitroBeaconUrl', '$url'); nitroData.append('nitroBeaconCookies', '$cookiesb64'); nitroData.append('nitroBeaconHash', '$hash'); nitroData.append('proxyPurgeOnly', '$proxyPurgeOnly'); nitroData.append('layout', '$layout'); navigator.sendBeacon(location.href, nitroData);
1049 } else {
1050 var xhr = new XMLHttpRequest(); xhr.open('POST', location.href, true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); xhr.send('nitroBeaconUrl={$url}&nitroBeaconCookies={$cookiesb64}&nitroBeaconHash={$hash}&proxyPurgeOnly={$proxyPurgeOnly}&layout={$layout}');
1051 }
1052 }
1053 </script>";
1054 }
1055 }
1056 }
1057
1058 function nitropack_print_cookie_handler_script() {
1059
1060 if ( defined( "NITROPACK_COOKIE_HANDLER_PRINTED" ) )
1061 return;
1062 define( "NITROPACK_COOKIE_HANDLER_PRINTED", true );
1063
1064 echo apply_filters( "nitro_script_output", nitropack_get_cookie_handler_script() );
1065 }
1066
1067 function nitropack_get_cookie_handler_script() {
1068 return "
1069 <script nitro-exclude>
1070 document.cookie = 'nitroCachedPage=' + (!window.NITROPACK_STATE ? '0' : '1') + '; path=/; SameSite=Lax';
1071 </script>";
1072 }
1073
1074 function nitropack_print_generic_nitro_script() {
1075 if ( defined( "NITROPACK_GENERIC_NITRO_SCRIPT_PRINTED" ) )
1076 return;
1077 define( "NITROPACK_GENERIC_NITRO_SCRIPT_PRINTED", true );
1078 echo apply_filters( "nitro_script_output", nitropack_get_telemetry_meta() );
1079 echo apply_filters( "nitro_script_output", nitropack_get_generic_nitro_script() );
1080 }
1081
1082 function nitropack_get_generic_nitro_script() {
1083 $siteConfig = nitropack_get_site_config();
1084 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) && ! empty( $siteConfig["siteSecret"] ) ) {
1085 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
1086 $config = $nitro->getConfig();
1087 if ( ! empty( $config->GenericNitroScript->Script ) ) {
1088 return "<script id='nitro-generic' nitro-exclude>" . $config->GenericNitroScript->Script . "</script>";
1089 }
1090 }
1091 }
1092
1093 return "";
1094 }
1095
1096 function nitropack_get_telemetry_meta() {
1097 $disabledReason = get_nitropack()->getDisabledReason();
1098 $missReason = $disabledReason !== NULL ? $disabledReason : "cache not found";
1099 $pageType = get_nitropack()->getPageType();
1100 $isEligibleForOptimization = nitropack_passes_page_requirements();
1101 $metaObj = "window.NPTelemetryMetadata={";
1102
1103 if ( $missReason ) {
1104 $metaObj .= "missReason: (!window.NITROPACK_STATE ? '$missReason' : 'hit'),";
1105 }
1106
1107 if ( $pageType ) {
1108 $metaObj .= "pageType: '$pageType',";
1109 }
1110
1111 $metaObj .= "isEligibleForOptimization: " . ( $isEligibleForOptimization ? "true" : "false" ) . ",";
1112
1113 $metaObj .= "}";
1114
1115 return "<script id='nitro-telemetry-meta' nitro-exclude>$metaObj</script>";
1116 }
1117
1118 function nitropack_print_element_override() {
1119 if ( defined( "NITROPACK_ELEMENT_OVERRIDE_PRINTED" ) )
1120 return;
1121 define( "NITROPACK_ELEMENT_OVERRIDE_PRINTED", true );
1122 echo apply_filters( "nitro_script_output", nitropack_get_element_override_script() );
1123 }
1124
1125 function nitropack_get_element_override_script() {
1126 $nitro = get_nitropack_sdk();
1127 return $nitro !== NULL ? $nitro->getStatefulCacheHandlerScript() : "";
1128 }
1129
1130 function nitropack_has_advanced_cache() {
1131 return defined( 'NITROPACK_ADVANCED_CACHE' );
1132 }
1133
1134 function nitropack_validate_site_id( $siteId ) {
1135 return preg_match( "/^([a-zA-Z]{32})$/", trim( $siteId ) );
1136 }
1137
1138 function nitropack_validate_site_secret( $siteSecret ) {
1139 return preg_match( "/^([a-zA-Z0-9]{64})$/", trim( $siteSecret ) );
1140 }
1141
1142 function nitropack_validate_webhook_token( $token ) {
1143 return preg_match( "/^([abcdef0-9]{32})$/", strtolower( $token ) );
1144 }
1145
1146 function nitropack_validate_wc_currency( $cookieValue ) {
1147 return preg_match( "/^([a-z]{3})$/", strtolower( $cookieValue ) );
1148 }
1149
1150 function nitropack_validate_wc_currency_language( $cookieValue ) {
1151 return preg_match( "/^([a-z_\\-]{2,})$/", strtolower( $cookieValue ) );
1152 }
1153
1154 function nitropack_is_advanced_cache_allowed() {
1155 return ! in_array( nitropack_detect_hosting(), array(
1156 "pressable"
1157 ) );
1158 }
1159
1160 function nitropack_get_hosting_notice_file() {
1161 return nitropack_trailingslashit( NITROPACK_DATA_DIR ) . "hosting_notice";
1162 }
1163
1164 function nitropack_dismiss_hosting_notice() {
1165 nitropack_verify_ajax_nonce( $_REQUEST );
1166 $hostingNoticeFile = nitropack_get_hosting_notice_file();
1167 if ( WP_DEBUG ) {
1168 touch( $hostingNoticeFile );
1169 } else {
1170 @touch( $hostingNoticeFile );
1171 }
1172 }
1173
1174 function nitropack_is_config_up_to_date() {
1175 $siteConfig = nitropack_get_site_config();
1176 return ! empty( $siteConfig ) && ! empty( $siteConfig["pluginVersion"] ) && $siteConfig["pluginVersion"] == NITROPACK_VERSION;
1177 }
1178
1179 function nitropack_filter_non_original_cookies( &$cookies ) {
1180 global $np_originalRequestCookies;
1181 $ogNames = is_array( $np_originalRequestCookies ) ? array_keys( $np_originalRequestCookies ) : array();
1182 foreach ( $cookies as $name => $val ) {
1183 if ( ! in_array( $name, $ogNames ) ) {
1184 unset( $cookies[ $name ] );
1185 }
1186 }
1187 }
1188
1189 function get_nitropack_sdk( $siteId = null, $siteSecret = null, $urlOverride = NULL, $forwardExceptions = false ) {
1190 return get_nitropack()->getSdk( $siteId, $siteSecret, $urlOverride, $forwardExceptions );
1191 }
1192
1193 function get_nitropack_integration_url( $integration, $nitro = null ) {
1194 if ( $nitro || ( null !== $nitro = get_nitropack_sdk() ) ) {
1195 return $nitro->integrationUrl( $integration );
1196 }
1197
1198 return "#";
1199 }
1200
1201 function nitropack_get_layout() {
1202 $layout = "default";
1203
1204 if ( nitropack_is_home() ) {
1205 $layout = "home";
1206 } else if ( nitropack_is_blogindex() ) {
1207 $layout = "blogindex";
1208 } else if ( is_page() ) {
1209 $layout = "page";
1210 } else if ( is_attachment() ) {
1211 $layout = "attachment";
1212 } else if ( is_author() ) {
1213 $layout = "author";
1214 } else if ( is_search() ) {
1215 $layout = "search";
1216 } else if ( is_tag() ) {
1217 $layout = "tag";
1218 } else if ( is_tax() ) {
1219 $layout = "taxonomy";
1220 } else if ( is_category() ) {
1221 $layout = "category";
1222 } else if ( nitropack_is_archive() ) {
1223 $layout = "archive";
1224 } else if ( is_feed() ) {
1225 $layout = "feed";
1226 } else if ( is_page() ) {
1227 $layout = "page";
1228 } else if ( is_single() ) {
1229 $layout = get_post_type();
1230 }
1231
1232 return $layout;
1233 }
1234
1235 function nitropack_sdk_invalidate( $url = NULL, $tag = NULL, $reason = NULL ) {
1236
1237 $status = false;
1238
1239 if ( null !== $nitro = get_nitropack_sdk() ) {
1240 try {
1241 $siteConfig = nitropack_get_site_config();
1242 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
1243
1244 if ( $tag ) {
1245 if ( is_array( $tag ) ) {
1246 $tag = array_map( 'nitropack_filter_tag', $tag );
1247 } else {
1248 $tag = nitropack_filter_tag( $tag );
1249 }
1250 }
1251
1252 $nitro->invalidateCache( $url, $tag, $reason );
1253
1254 try {
1255
1256 if ( defined( 'NITROPACK_DEBUG_MODE' ) ) {
1257 do_action( 'nitropack_debug_invalidate', $url, $tag, $reason );
1258 }
1259
1260 do_action( 'nitropack_integration_purge_url', $homeUrl );
1261
1262 if ( $tag ) {
1263 do_action( 'nitropack_integration_purge_all' );
1264 } else if ( $url ) {
1265 do_action( 'nitropack_integration_purge_url', $url );
1266 } else {
1267 do_action( 'nitropack_integration_purge_all' );
1268 }
1269 } catch (\Exception $e) {
1270 // Exception while signaling 3rd party integration addons to purge their cache
1271 }
1272 } catch (\Exception $e) {
1273 $status = false;
1274 }
1275
1276 $status = true;
1277 }
1278
1279 return $status;
1280 }
1281
1282 /* Start Heartbeat Related Functions */
1283 function nitropack_is_heartbeat_needed() {
1284 return ! nitropack_is_optimizer_request() &&
1285 ! nitropack_is_amp_page() &&
1286 ! nitropack_is_heartbeat_running() &&
1287 ( ! nitropack_is_heartbeat_completed() || time() - nitropack_last_heartbeat() > NITROPACK_HEARTBEAT_INTERVAL );
1288 }
1289
1290 function nitropack_print_heartbeat_script() {
1291 if ( nitropack_is_heartbeat_needed() ) {
1292 if ( defined( "NITROPACK_HEARTBEAT_PRINTED" ) )
1293 return;
1294 define( "NITROPACK_HEARTBEAT_PRINTED", true );
1295 echo apply_filters( "nitro_script_output", nitropack_get_heartbeat_script() );
1296 }
1297 }
1298
1299 function nitropack_get_heartbeat_script() {
1300 $siteConfig = nitropack_get_site_config();
1301 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) && ! empty( $siteConfig["siteSecret"] ) ) {
1302 if ( null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
1303 if ( is_admin() ) {
1304 $credentials = "same-origin";
1305 } else {
1306 $credentials = "omit";
1307 }
1308
1309 return "
1310 <script nitro-exclude>
1311 var heartbeatData = new FormData(); heartbeatData.append('nitroHeartbeat', '1');
1312 fetch(location.href, {method: 'POST', body: heartbeatData, credentials: '$credentials'});
1313 </script>";
1314 }
1315 }
1316 }
1317
1318 function is_valid_nitropack_heartbeat() {
1319 return ! empty( $_POST['nitroHeartbeat'] );
1320 }
1321
1322 function nitropack_get_heartbeat_file() {
1323 if ( null !== $nitro = get_nitropack_sdk() ) {
1324 return nitropack_trailingslashit( $nitro->getCacheDir() ) . "heartbeat";
1325 } else {
1326 return nitropack_trailingslashit( NITROPACK_DATA_DIR ) . "heartbeat";
1327 }
1328 }
1329
1330 function nitropack_last_heartbeat() {
1331 if ( null !== $nitro = get_nitropack_sdk() ) {
1332 try {
1333 return \NitroPack\SDK\Filesystem::fileMTime( nitropack_get_heartbeat_file() );
1334 } catch (\Exception $e) {
1335 return 0;
1336 }
1337 }
1338 }
1339
1340 function nitropack_is_heartbeat_running() {
1341 if ( null !== $nitro = get_nitropack_sdk() ) {
1342 try {
1343 $heartbeatContent = \NitroPack\SDK\Filesystem::fileGetContents( nitropack_get_heartbeat_file() );
1344 if ( $heartbeatContent == "1" ) {
1345 return time() - nitropack_last_heartbeat() < NITROPACK_HEARTBEAT_INTERVAL;
1346 }
1347 } catch (\Exception $e) {
1348 return false;
1349 }
1350 }
1351 }
1352
1353 function nitropack_is_heartbeat_completed() {
1354 if ( null !== $nitro = get_nitropack_sdk() ) {
1355 try {
1356 $heartbeatContent = \NitroPack\SDK\Filesystem::fileGetContents( nitropack_get_heartbeat_file() );
1357 return $heartbeatContent == "0"; // 0 - Job Done, 1 - Job Running, 2 - Job Needs Repeat
1358 } catch (\Exception $e) {
1359 return true;
1360 }
1361 }
1362 }
1363
1364 function nitropack_handle_heartbeat() {
1365 // TODO: Lock the file before checking this
1366 if ( nitropack_is_heartbeat_running() )
1367 return;
1368
1369 session_write_close();
1370 if ( null !== $nitro = get_nitropack_sdk() ) {
1371 try {
1372 $success = true;
1373 \NitroPack\SDK\Filesystem::filePutContents( nitropack_get_heartbeat_file(), 1 );
1374 if ( nitropack_healthcheck() ) {
1375 $success &= nitropack_flush_backlog();
1376 }
1377 $success &= nitropack_cache_cleanup();
1378
1379 if ( $success ) {
1380 \NitroPack\SDK\Filesystem::filePutContents( nitropack_get_heartbeat_file(), 0 );
1381 } else {
1382 \NitroPack\SDK\Filesystem::filePutContents( nitropack_get_heartbeat_file(), 2 );
1383 }
1384 } catch (\Exception $e) {
1385 return false;
1386 }
1387 }
1388 exit;
1389 }
1390
1391 function nitropack_healthcheck() {
1392 if ( null !== $nitro = get_nitropack_sdk() ) {
1393 return $nitro->getHealthStatus() == \NitroPack\SDK\HealthStatus::HEALTHY || $nitro->checkHealthStatus() == \NitroPack\SDK\HealthStatus::HEALTHY;
1394 }
1395 return true;
1396 }
1397
1398 function nitropack_flush_backlog() {
1399 if ( null !== $nitro = get_nitropack_sdk() ) {
1400 try {
1401 if ( $nitro->backlog->exists() ) {
1402 return $nitro->backlog->replay( 30 );
1403 }
1404 } catch (\NitroPack\SDK\BacklogReplayTimeoutException $e) {
1405 $nitro->backlog->delete();
1406 return nitropack_sdk_purge( NULL, NULL, "Full purge after backlog timeout" );
1407 } catch (\Exception $e) {
1408 return false;
1409 }
1410 }
1411 return true;
1412 }
1413
1414 function nitropack_cache_cleanup() {
1415 if ( null !== $nitro = get_nitropack_sdk() ) {
1416 $cacheDirParent = dirname( $nitro->getCacheDir() );
1417 $entries = scandir( $cacheDirParent );
1418 foreach ( $entries as $entry ) {
1419 if ( strpos( $entry, ".stale." ) !== false ) {
1420 $cacheDir = nitropack_trailingslashit( $cacheDirParent ) . $entry;
1421 try {
1422 \NitroPack\SDK\Filesystem::deleteDir( $cacheDir );
1423 } catch (\Exception $e) {
1424 // TODO: Log this
1425 return false;
1426 }
1427 }
1428 }
1429 }
1430 return true;
1431 }
1432 /* End Heartbeat Related Functions */
1433
1434 function nitropack_sdk_purge( $url = NULL, $tag = NULL, $reason = NULL, $type = \NitroPack\SDK\PurgeType::COMPLETE ) {
1435
1436 $status = false;
1437
1438 if ( null !== $nitro = get_nitropack_sdk() ) {
1439 try {
1440 $siteConfig = nitropack_get_site_config();
1441 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
1442
1443 if ( $tag ) {
1444 if ( is_array( $tag ) ) {
1445 $tag = array_map( 'nitropack_filter_tag', $tag );
1446 } else {
1447 $tag = nitropack_filter_tag( $tag );
1448 }
1449 }
1450
1451 if ( ! $url && ! $tag ) {
1452 $nitro->purgeLocalCache( true );
1453 }
1454
1455 $nitro->purgeCache( $url, $tag, $type, $reason );
1456
1457 if ( defined( 'NITROPACK_DEBUG_MODE' ) ) {
1458 do_action( 'nitropack_debug_purge', $url, $tag, $reason );
1459 }
1460
1461 try {
1462 do_action( 'nitropack_integration_purge_url', $homeUrl );
1463
1464 if ( $tag ) {
1465 do_action( 'nitropack_integration_purge_all' );
1466 } else if ( $url ) {
1467 do_action( 'nitropack_integration_purge_url', $url );
1468 } else {
1469 do_action( 'nitropack_integration_purge_all' );
1470 }
1471 } catch (\Exception $e) {
1472 // Exception while signaling 3rd party integration addons to purge their cache
1473 }
1474 } catch (\Exception $e) {
1475 $status = false;
1476 }
1477
1478 $status = true;
1479 }
1480
1481 return $status;
1482 }
1483
1484 /**
1485 * @param string|null $url
1486 * @return bool
1487 */
1488 function nitropack_sdk_purge_local( $url = NULL ) {
1489 if ( null === $nitro = get_nitropack_sdk() ) {
1490 return false;
1491 }
1492
1493 try {
1494 if ( $url ) {
1495 $nitro->purgeLocalUrlCache( $url );
1496 do_action( 'nitropack_integration_purge_url', $url );
1497 return true;
1498 }
1499
1500 $nitro->purgeLocalCache( true );
1501
1502 try {
1503 do_action( 'nitropack_integration_purge_all' );
1504 } catch (\Exception $e) {
1505 // Exception while signaling our 3rd party integration addons to purge their cache
1506 }
1507
1508 return true;
1509 } catch (\Exception $e) {
1510 return false;
1511 }
1512 }
1513
1514 /**
1515 * @param string|null $url
1516 * @return bool
1517 */
1518 function nitropack_sdk_invalidate_local( $url = NULL ) {
1519 if ( null === $nitro = get_nitropack_sdk() ) {
1520 return false;
1521 }
1522
1523 try {
1524 if ( $url ) {
1525 $nitro->invalidateLocalUrlCache( $url );
1526 do_action( 'nitropack_integration_purge_url', $url );
1527 return true;
1528 }
1529
1530 $nitro->invalidateLocalCache( true );
1531
1532 try {
1533 do_action( 'nitropack_integration_purge_all' );
1534 } catch (\Exception $e) {
1535 // Exception while signaling our 3rd party integration addons to purge their cache
1536 }
1537
1538 return true;
1539 } catch (\Exception $e) {
1540 return false;
1541 }
1542 }
1543
1544 function nitropack_sdk_delete_backlog() {
1545 if ( null !== $nitro = get_nitropack_sdk() ) {
1546 try {
1547 if ( $nitro->backlog->exists() ) {
1548 $nitro->backlog->delete();
1549 }
1550 } catch (\Exception $e) {
1551 return false;
1552 }
1553
1554 return true;
1555 }
1556
1557 return false;
1558 }
1559
1560 function nitropack_purge( $url = NULL, $tag = NULL, $reason = NULL ) {
1561 if ( $tag != "pageType:home" ) {
1562 $siteConfig = nitropack_get_site_config();
1563 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
1564 nitropack_log_invalidate( $homeUrl, "pageType:home", $reason );
1565 }
1566
1567 if ( $tag != "pageType:archive" ) {
1568 nitropack_log_invalidate( NULL, "pageType:archive", $reason );
1569 }
1570
1571 nitropack_log_purge( $url, $tag, $reason );
1572 }
1573
1574 function nitropack_log_purge( $url = NULL, $tag = NULL, $reason = NULL ) {
1575 global $np_loggedPurges;
1576 if ( $tag && is_array( $tag ) ) {
1577 foreach ( $tag as $tagSingle ) {
1578 nitropack_log_purge( $url, $tagSingle, $reason );
1579 }
1580 return;
1581 }
1582
1583 $keyBase = "";
1584 if ( $url ) {
1585 $keyBase .= $url;
1586 }
1587
1588 if ( $tag ) {
1589 $tag = nitropack_filter_tag( $tag );
1590 $keyBase .= $tag;
1591 }
1592
1593 $purgeRequestKey = md5( $keyBase );
1594 if ( is_array( $np_loggedPurges ) && array_key_exists( $purgeRequestKey, $np_loggedPurges ) ) {
1595 $np_loggedPurges[ $purgeRequestKey ]["reason"] = $reason;
1596 $np_loggedPurges[ $purgeRequestKey ]["priority"]++;
1597 } else {
1598 $np_loggedPurges[ $purgeRequestKey ] = array(
1599 "url" => $url,
1600 "tag" => $tag,
1601 "reason" => $reason,
1602 "priority" => 1
1603 );
1604 }
1605 }
1606
1607 function nitropack_invalidate( $url = NULL, $tag = NULL, $reason = NULL ) {
1608 if ( $tag != "pageType:home" ) {
1609 $siteConfig = nitropack_get_site_config();
1610 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
1611 nitropack_log_invalidate( $homeUrl, "pageType:home", $reason );
1612 }
1613
1614 if ( $tag != "pageType:archive" ) {
1615 nitropack_log_invalidate( NULL, "pageType:archive", $reason );
1616 }
1617
1618 nitropack_log_invalidate( $url, $tag, $reason );
1619 }
1620
1621 function nitropack_log_invalidate( $url = NULL, $tag = NULL, $reason = NULL ) {
1622 global $np_loggedInvalidations;
1623 if ( $tag && is_array( $tag ) ) {
1624 foreach ( $tag as $tagSingle ) {
1625 nitropack_log_invalidate( $url, $tagSingle, $reason );
1626 }
1627 return;
1628 }
1629
1630 $keyBase = "";
1631 if ( $url ) {
1632 $keyBase .= $url;
1633 }
1634
1635 if ( $tag ) {
1636 $tag = nitropack_filter_tag( $tag );
1637 $keyBase .= $tag;
1638 }
1639
1640 $invalidateRequestKey = md5( $keyBase );
1641 if ( is_array( $np_loggedInvalidations ) && array_key_exists( $invalidateRequestKey, $np_loggedInvalidations ) ) {
1642 $np_loggedInvalidations[ $invalidateRequestKey ]["reason"] = $reason;
1643 $np_loggedInvalidations[ $invalidateRequestKey ]["priority"]++;
1644 } else {
1645 $np_loggedInvalidations[ $invalidateRequestKey ] = array(
1646 "url" => $url,
1647 "tag" => $tag,
1648 "reason" => $reason,
1649 "priority" => 1
1650 );
1651 }
1652 }
1653
1654 function nitropack_queue_sort( $a, $b ) {
1655 if ( $a["priority"] == $b["priority"] ) {
1656 return 0;
1657 }
1658 return ( $a["priority"] < $b["priority"] ) ? -1 : 1;
1659 }
1660
1661 function nitropack_execute_purges() {
1662 global $np_loggedPurges;
1663
1664 if ( ! empty( $np_loggedPurges ) ) {
1665 uasort( $np_loggedPurges, "nitropack_queue_sort" );
1666 foreach ( $np_loggedPurges as $requestKey => $data ) {
1667 nitropack_sdk_purge( $data["url"], $data["tag"], $data["reason"] );
1668 }
1669 }
1670 }
1671
1672 function nitropack_execute_invalidations() {
1673 global $np_loggedInvalidations;
1674
1675 if ( ! empty( $np_loggedInvalidations ) ) {
1676 uasort( $np_loggedInvalidations, "nitropack_queue_sort" );
1677 foreach ( $np_loggedInvalidations as $requestKey => $data ) {
1678 nitropack_sdk_invalidate( $data["url"], $data["tag"], $data["reason"] );
1679 }
1680 }
1681 }
1682
1683 function nitropack_execute_warmups() {
1684 if ( ! empty( $_GET["action"] ) && ( $_GET["action"] === "edit" ) && ! empty( $_GET["meta-box-loader"] ) ) {
1685 return;
1686 }
1687
1688 try {
1689 if ( ! empty( \NitroPack\WordPress\NitroPack::$np_loggedWarmups ) && ( null !== $nitro = get_nitropack_sdk() ) ) {
1690 $warmupStats = $nitro->getApi()->getWarmupStats();
1691 if ( ! empty( $warmupStats["status"] ) ) {
1692 foreach ( array_unique( \NitroPack\WordPress\NitroPack::$np_loggedWarmups ) as $url ) {
1693 $nitro->getApi()->runWarmup( $url );
1694 }
1695 }
1696 }
1697 } catch (\Exception $e) {
1698 }
1699 }
1700
1701 function nitropack_fetch_config() {
1702 if ( null !== $nitro = get_nitropack_sdk() ) {
1703 try {
1704 $nitro->fetchConfig();
1705 } catch (\Exception $e) {
1706 }
1707 }
1708 }
1709
1710 function nitropack_theme_handler( $event = NULL ) {
1711 if ( ! get_option( "nitropack-autoCachePurge", 1 ) )
1712 return;
1713
1714 $msg = $event ? $event : 'Theme switched';
1715
1716 try {
1717 nitropack_sdk_purge( NULL, NULL, $msg ); // purge entire cache
1718 } catch (\Exception $e) {
1719 }
1720 }
1721
1722 function nitropack_json_and_exit( $array ) {
1723 if ( nitropack_is_wp_cli() ) {
1724 $type = NULL;
1725 if ( array_key_exists( "status", $array ) ) {
1726 $type = $array["status"];
1727 } else if ( array_key_exists( "type", $array ) ) {
1728 $type = $array["type"];
1729 }
1730
1731 if ( $type && array_key_exists( "message", $array ) ) {
1732 if ( $type == "success" ) {
1733 WP_CLI::success( $array["message"] );
1734 } else {
1735 WP_CLI::error( $array["message"] );
1736 }
1737 }
1738 } else {
1739 echo json_encode( $array );
1740 }
1741 exit;
1742 }
1743 function nitropack_admin_toast_msgs( $type ) {
1744 if ( $type === 'success' ) {
1745 $msg = esc_html__( 'Settings updated.', 'nitropack' );
1746 } else {
1747 $msg = esc_html__( 'Something went wrong.', 'nitropack' );
1748 }
1749 return $msg;
1750 }
1751 /* General verification for AJAX requests
1752 * @params array $request_data The request data
1753 * @params array|null $allowed_roles The allowed user roles
1754 */
1755 function nitropack_verify_ajax_nonce( $request_data, $allowed_roles = null ) {
1756 // If not an ajax request
1757 if ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) {
1758 return;
1759 }
1760
1761 // Check if WordPress functions are available
1762 if ( ! function_exists( 'wp_verify_nonce' ) || ! function_exists( 'wp_die' ) || ! function_exists( 'current_user_can' ) ) {
1763 return;
1764 }
1765
1766 // If nonce fails verification
1767 if ( empty( $request_data['nonce'] ) || ! wp_verify_nonce( $request_data['nonce'], NITROPACK_NONCE ) ) {
1768 wp_die( 'Unauthorized request' );
1769 }
1770
1771 // Check user permissions
1772 if ( $allowed_roles ) {
1773 $has_permission = false;
1774 foreach ( $allowed_roles as $role ) {
1775 if ( current_user_can( $role ) ) {
1776 $has_permission = true;
1777 break;
1778 }
1779 }
1780 if ( ! $has_permission ) {
1781 wp_die( 'Unauthorized request' );
1782 }
1783 } else {
1784 //fallback to admin rights
1785 if ( ! current_user_can( 'manage_options' ) ) {
1786 wp_die( 'Unauthorized request' );
1787 }
1788 }
1789 }
1790 function nitropack_has_post_important_change( $post ) {
1791 $prevPost = nitropack_get_post_pre_update( $post );
1792 return $prevPost && ( $prevPost->post_title != $post->post_title || $prevPost->post_name != $post->post_name || $prevPost->post_excerpt != $post->post_excerpt );
1793 }
1794
1795
1796 function nitropack_clean_post_cache( $post, $taxonomies = NULL, $hasImportantChangeInPost = NULL, $reason = NULL, $usePurge = false ) {
1797 try {
1798 $postID = $post->ID;
1799 $postType = isset( $post->post_type ) ? $post->post_type : "post";
1800 $nicePostTypeLabel = nitropack_get_nice_post_type_label( $postType );
1801 $reason = $reason ? $reason : sprintf( "Updated %s '%s'", $nicePostTypeLabel, $post->post_title );
1802 $CPTOptimization = NitroPack\WordPress\Settings\CPTOptimization::getInstance();
1803 $cacheableObjectTypes = $CPTOptimization->nitropack_get_cacheable_object_types();
1804
1805 if ( in_array( $postType, $cacheableObjectTypes ) ) {
1806 if ( $usePurge ) {
1807 // We only purge the single pages because they have to immediately stop serving cache
1808 // These pages no longer exists and if their URL is requested we must not server cache
1809 nitropack_purge( NULL, "single:$postID", $reason );
1810 } else {
1811 nitropack_invalidate( NULL, "single:$postID", $reason );
1812 }
1813
1814 nitropack_invalidate( NULL, "post:$postID", $reason );
1815
1816 if ( $hasImportantChangeInPost === NULL ) {
1817 $hasImportantChangeInPost = nitropack_has_post_important_change( $post );
1818 }
1819 if ( $taxonomies === NULL ) {
1820 if ( $hasImportantChangeInPost ) { // This change should be reflected in all taxonomy pages
1821 $taxonomies = array( 'related' => nitropack_get_taxonomies( $post ) );
1822 } else { // No important change, so only update taxonomy pages which have been added or removed from the post
1823 $taxonomies = nitropack_get_taxonomies_for_update( $post );
1824 }
1825 }
1826 if ( $taxonomies ) {
1827 if ( ! empty( $taxonomies['added'] ) ) { // taxonomies that the post was just added to, must purge all pages for these taxonomies
1828 foreach ( $taxonomies['added'] as $term_taxonomy_id ) {
1829 nitropack_invalidate( NULL, "tax:$term_taxonomy_id", $reason );
1830 }
1831 }
1832 if ( ! empty( $taxonomies['deleted'] ) ) { // taxonomy pages that the post was just removed from (also accounts for paginations via the taxpost: tag instead of only tax:)
1833 foreach ( $taxonomies['deleted'] as $term_taxonomy_id ) {
1834 nitropack_invalidate( NULL, "taxpost:$term_taxonomy_id:$postID", $reason );
1835 }
1836 }
1837 if ( ! empty( $taxonomies['related'] ) ) { // taxonomy pages that the post is linked to (also accounts for paginations via the taxpost: tag instead of only tax:)
1838 foreach ( $taxonomies['related'] as $term_taxonomy_id ) {
1839 nitropack_invalidate( NULL, "taxpost:$term_taxonomy_id:$postID", $reason );
1840 }
1841 }
1842 }
1843 } else {
1844 if ( $post->public ) {
1845 nitropack_invalidate( NULL, "post:$postID", $reason );
1846 }
1847
1848 $posts = get_post_ancestors( $postID );
1849 foreach ( $posts as $parentID ) {
1850 $parent = get_post( $parentID );
1851 nitropack_clean_post_cache( $parent, false, false, $reason );
1852 }
1853 }
1854 } catch (\Exception $e) {
1855 }
1856 }
1857
1858 function nitropack_get_nice_post_type_label( $postType ) {
1859 $postTypes = get_post_types( array(
1860 "name" => $postType
1861 ), "objects" );
1862
1863 return ! empty( $postTypes[ $postType ] ) && ! empty( $postTypes[ $postType ]->labels ) ? $postTypes[ $postType ]->labels->singular_name : $postType;
1864 }
1865
1866 function nitropack_handle_comment_transition( $new, $old, $comment ) {
1867 if ( ! get_option( "nitropack-autoCachePurge", 1 ) )
1868 return;
1869
1870 try {
1871 $postID = $comment->comment_post_ID;
1872 $post = get_post( $postID );
1873 $postType = isset( $post->post_type ) ? $post->post_type : "post";
1874 $CPTOptimization = NitroPack\WordPress\Settings\CPTOptimization::getInstance();
1875 $cacheableObjectTypes = $CPTOptimization->nitropack_get_cacheable_object_types();
1876
1877 if ( in_array( $postType, $cacheableObjectTypes ) ) {
1878 nitropack_invalidate( NULL, "single:" . $postID, sprintf( "Invalidation of '%s' due to changing related comment status", $post->post_title ) );
1879 }
1880 } catch (\Exception $e) {
1881 // TODO: Log the error
1882 }
1883 }
1884
1885 function nitropack_handle_comment_post( $commentID, $isApproved ) {
1886 if ( ! get_option( "nitropack-autoCachePurge", 1 ) || $isApproved !== 1 )
1887 return;
1888
1889 try {
1890 $comment = get_comment( $commentID );
1891 $postID = $comment->comment_post_ID;
1892 $post = get_post( $postID );
1893 nitropack_invalidate( NULL, "single:" . $postID, sprintf( "Invalidation of '%s' due to posting a new approved comment", $post->post_title ) );
1894 } catch (\Exception $e) {
1895 // TODO: Log the error
1896 }
1897 }
1898
1899 function nitropack_detect_changes_and_clean_post_cache( $post ) {
1900 if ( ! get_option( "nitropack-autoCachePurge", 1 ) ) {
1901 return;
1902 }
1903
1904 $post_before = nitropack_get_post_pre_update( $post );
1905 $ignoredComparisonKeys = array( 'post_modified', 'post_modified_gmt' );
1906 $canCleanPostCache = false;
1907 $postStatesEqual = nitropack_compare_posts( (array) $post_before, (array) $post, $ignoredComparisonKeys );
1908
1909 if ( $postStatesEqual ) {
1910 $taxCurrent = nitropack_get_taxonomies( $post );
1911 $taxPreUpdate = nitropack_get_taxonomies_pre_update( $post );
1912 $taxAreEqual = nitropack_compare_posts( $taxCurrent, $taxPreUpdate );
1913 if ( $taxAreEqual ) {
1914 $metaCurrent = get_post_meta( $post->ID );
1915 $metaPreUpdate = nitropack_get_meta_pre_update( $post );
1916 $metaIsEqual = nitropack_compare_posts( $metaCurrent, $metaPreUpdate );
1917 if ( ! $metaIsEqual ) {
1918 $canCleanPostCache = true;
1919 }
1920 } else {
1921 $canCleanPostCache = true;
1922 }
1923 } else {
1924 $canCleanPostCache = true;
1925 }
1926
1927 if ( $canCleanPostCache ) {
1928 \NitroPack\WordPress\NitroPack::$np_loggedWarmups[] = get_permalink( $post );
1929 nitropack_clean_post_cache( $post );
1930 define( 'NITROPACK_PURGE_CACHE', true );
1931 }
1932 }
1933
1934
1935 function nitropack_handle_post_transition( $new, $old, $post ) {
1936 if ( wp_is_post_revision( $post ) )
1937 return;
1938 if ( ! empty( $post->ID ) && in_array( $post->ID, \NitroPack\WordPress\NitroPack::$ignoreUpdatePostIDs ) )
1939 return;
1940 if ( ! get_option( "nitropack-autoCachePurge", 1 ) )
1941 return;
1942
1943 try {
1944 if ( $new === "auto-draft" || ( $new === "draft" && $old === "auto-draft" ) || ( $new === "draft" && $old != "publish" ) || $new === "inherit" ) { // Creating a new post or draft, don't do anything for now.
1945 return;
1946 }
1947
1948 $ignoredPostTypes = array(
1949 "revision",
1950 "scheduled-action",
1951 "flamingo_contact",
1952 "carts"/*WooCommerce Cart Reports*/
1953 );
1954
1955 $nicePostTypes = array(
1956 "post" => "Post",
1957 "page" => "Page",
1958 "tribe_events" => "Calendar Event",
1959 );
1960 $postType = isset( $post->post_type ) ? $post->post_type : "post";
1961 $nicePostTypeLabel = nitropack_get_nice_post_type_label( $postType );
1962
1963 if ( in_array( $postType, $ignoredPostTypes ) )
1964 return;
1965
1966 switch ( $postType ) {
1967 case "nav_menu_item":
1968 nitropack_invalidate( NULL, NULL, sprintf( "Invalidation of all pages due to modifying menu entries" ) );
1969 break;
1970 case "customize_changeset":
1971 nitropack_invalidate( NULL, NULL, sprintf( "Invalidation of all pages due to applying appearance customization" ) );
1972 break;
1973 case "custom_css":
1974 nitropack_invalidate( NULL, NULL, sprintf( "Invalidation of all pages due to modifying custom CSS" ) );
1975 break;
1976 default:
1977 if ( $new == "future" ) {
1978 nitropack_clean_post_cache( $post, array( 'added' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to scheduling %s '%s'", $nicePostTypeLabel, $post->post_title ) );
1979 } else if ( $new === 'publish' && $old === 'trash' ) {
1980 nitropack_clean_post_cache( $post, array( 'added' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to restoring %s '%s'", $nicePostTypeLabel, $post->post_title ) );
1981 } else if ( $new == "publish" && $old != "publish" ) {
1982 /* Handle first publish */
1983 \NitroPack\WordPress\NitroPack::$np_loggedWarmups[] = get_permalink( $post->ID );
1984 if ( ! defined( 'NITROPACK_PURGE_CACHE' ) ) {
1985 nitropack_clean_post_cache( $post, array( 'added' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to publishing %s '%s'", $nicePostTypeLabel, $post->post_title ), true );
1986 }
1987 if ( $post->post_type === 'post' ) {
1988 nitropack_invalidate( NULL, "pageType:blogindex", 'Invalidation of blog page due to changing related post status' );
1989 }
1990 } else if ( $new == "trash" && $old == "publish" ) {
1991 nitropack_clean_post_cache( $post, array( 'deleted' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to deleting %s '%s'", $nicePostTypeLabel, $post->post_title ), true );
1992 } else if ( $new == "private" && $old == "publish" ) {
1993 nitropack_clean_post_cache( $post, array( 'deleted' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to making %s '%s' private", $nicePostTypeLabel, $post->post_title ), true );
1994 } else if ( $new == "draft" && $old == "publish" ) {
1995 nitropack_clean_post_cache( $post, array( 'deleted' => nitropack_get_taxonomies( $post ) ), true, sprintf( "Invalidate related pages due to making %s '%s' a draft", $nicePostTypeLabel, $post->post_title ), true );
1996 } else if ( $new != "trash" ) {
1997 if ( ! defined( 'NITROPACK_PURGE_CACHE' ) ) {
1998 nitropack_detect_changes_and_clean_post_cache( $post );
1999 }
2000 if ( $new == 'publish' ) {
2001 \NitroPack\WordPress\NitroPack::$np_loggedWarmups[] = get_permalink( $post->ID );
2002 }
2003 }
2004 break;
2005 }
2006 } catch (\Exception $e) {
2007 // TODO: Log the error
2008 }
2009 }
2010
2011 function nitropack_post_link_listener( $permalink, $post, $leavename ) {
2012 if ( is_object( $post ) ) {
2013 nitropack_handle_the_post( $post );
2014 }
2015
2016 return $permalink;
2017 }
2018
2019 function nitropack_handle_the_post( $post ) {
2020 global $np_customExpirationTimes, $np_queriedObj;
2021 if ( defined( 'POSTEXPIRATOR_VERSION' ) ) {
2022 $postExpiryDate = get_post_meta( $post->ID, "_expiration-date", true );
2023 if ( ! empty( $postExpiryDate ) && $postExpiryDate > time() ) { // We only need to look at future dates
2024 $np_customExpirationTimes[] = $postExpiryDate;
2025 }
2026 }
2027
2028 if ( function_exists( "sort_portfolio" ) ) { // Portfolio Sorting plugin
2029 $portfolioStartDate = get_post_meta( $post->ID, "start_date", true );
2030 $portfolioEndDate = get_post_meta( $post->ID, "end_date", true );
2031 if ( ! empty( $portfolioStartDate ) && strtotime( $portfolioStartDate ) > time() ) { // We only need to look at future dates
2032 $np_customExpirationTimes[] = strtotime( $portfolioStartDate );
2033 } else if ( ! empty( $portfolioEndDate ) && strtotime( $portfolioEndDate ) > time() ) { // We only need to look at future dates
2034 $np_customExpirationTimes[] = strtotime( $portfolioEndDate );
2035 }
2036 }
2037
2038 $GLOBALS["NitroPack.tags"][ "post:" . $post->ID ] = 1;
2039 $GLOBALS["NitroPack.tags"][ "author:" . $post->post_author ] = 1;
2040 if ( $np_queriedObj ) {
2041 $GLOBALS["NitroPack.tags"][ "taxpost:" . $np_queriedObj->term_taxonomy_id . ":" . $post->ID ] = 1;
2042 }
2043 }
2044
2045 function nitropack_ignore_post_updates( $postID ) {
2046 \NitroPack\WordPress\NitroPack::$ignoreUpdatePostIDs[] = $postID;
2047 }
2048
2049 function nitropack_get_taxonomies( $post ) {
2050 $term_taxonomy_ids = array();
2051 $taxonomies = get_object_taxonomies( $post->post_type );
2052 foreach ( $taxonomies as $taxonomy ) {
2053 $terms = get_the_terms( $post->ID, $taxonomy );
2054 if ( ! empty( $terms ) ) {
2055 foreach ( $terms as $term ) {
2056 $term_taxonomy_ids[] = $term->term_taxonomy_id;
2057 }
2058 }
2059 }
2060 return $term_taxonomy_ids;
2061 }
2062
2063 function nitropack_get_taxonomies_for_update( $post ) {
2064 $prevTaxonomies = nitropack_get_taxonomies_pre_update( $post );
2065 $newTaxonomies = nitropack_get_taxonomies( $post );
2066 $intersection = array_intersect( $newTaxonomies, $prevTaxonomies );
2067 $prevTaxonomies = array_diff( $prevTaxonomies, $intersection );
2068 $newTaxonomies = array_diff( $newTaxonomies, $intersection );
2069 return array(
2070 "added" => array_diff( $newTaxonomies, $prevTaxonomies ),
2071 "deleted" => array_diff( $prevTaxonomies, $newTaxonomies )
2072 );
2073 }
2074
2075 function nitropack_get_post_pre_update( $post ) {
2076 return ! empty( \NitroPack\WordPress\NitroPack::$preUpdatePosts[ $post->ID ] ) ? \NitroPack\WordPress\NitroPack::$preUpdatePosts[ $post->ID ] : NULL;
2077 }
2078
2079 function nitropack_get_taxonomies_pre_update( $post ) {
2080 return ! empty( \NitroPack\WordPress\NitroPack::$preUpdateTaxonomies[ $post->ID ] ) ? \NitroPack\WordPress\NitroPack::$preUpdateTaxonomies[ $post->ID ] : array();
2081 }
2082
2083 function nitropack_get_meta_pre_update( $post ) {
2084 return ! empty( \NitroPack\WordPress\NitroPack::$preUpdateMeta[ $post->ID ] ) ? \NitroPack\WordPress\NitroPack::$preUpdateMeta[ $post->ID ] : array();
2085 }
2086
2087 function nitropack_log_post_pre_update( $postID ) {
2088 if ( in_array( $postID, \NitroPack\WordPress\NitroPack::$ignoreUpdatePostIDs ) )
2089 return;
2090
2091
2092 $post = get_post( $postID );
2093 \NitroPack\WordPress\NitroPack::$preUpdatePosts[ $postID ] = $post;
2094 \NitroPack\WordPress\NitroPack::$preUpdateTaxonomies[ $postID ] = nitropack_get_taxonomies( $post );
2095 //Is post meta updated at this point? Or maybe this block should be moved to a different action?
2096 \NitroPack\WordPress\NitroPack::$preUpdateMeta[ $postID ] = get_post_meta( $postID );
2097 }
2098
2099 function nitropack_log_product_pre_api_update( $product, $request, $creating ) {
2100
2101 if ( ! $creating ) {
2102
2103 $postID = $product->get_id();
2104 if ( in_array( $postID, \NitroPack\WordPress\NitroPack::$ignoreUpdatePostIDs ) )
2105 return;
2106
2107
2108 $post = get_post( $postID );
2109 \NitroPack\WordPress\NitroPack::$preUpdatePosts[ $postID ] = $post;
2110 \NitroPack\WordPress\NitroPack::$preUpdateTaxonomies[ $postID ] = nitropack_get_taxonomies( $post );
2111 //Is post meta updated at this point? Or maybe this block should be moved to a different action?
2112 \NitroPack\WordPress\NitroPack::$preUpdateMeta[ $postID ] = get_post_meta( $postID );
2113 }
2114
2115 return $product;
2116 }
2117
2118 function nitropack_compare_posts( array $p1, array $p2, $ignoredKeys = null ) {
2119 $p1keys = array_keys( $p1 );
2120 $p2keys = array_keys( $p2 );
2121 if ( count( $p1keys ) !== count( $p2keys ) ) {
2122 return false;
2123 }
2124 if ( array_diff( $p1keys, $p2keys ) ) {
2125 return false;
2126 }
2127
2128 $isP1assoc = false;
2129 $expectedKey = 0;
2130 foreach ( $p2 as $i => $_ ) {
2131 if ( $i !== $expectedKey ) {
2132 $isP1assoc = true;
2133 }
2134 $expectedKey++;
2135 }
2136
2137 $isP2assoc = false;
2138 $expectedKey = 0;
2139 foreach ( $p2 as $i => $_ ) {
2140 if ( $i !== $expectedKey ) {
2141 $isP2assoc = true;
2142 }
2143 $expectedKey++;
2144 }
2145
2146 if ( $isP1assoc !== $isP2assoc ) {
2147 return false;
2148 }
2149
2150 if ( ! $isP1assoc && ! $isP2assoc ) {
2151 sort( $p1 );
2152 sort( $p2 );
2153 }
2154
2155 foreach ( $p1 as $poKey => $poVal ) {
2156 if ( $ignoredKeys && in_array( $poKey, $ignoredKeys, true ) ) {
2157 continue;
2158 }
2159 $checkpoint01 = is_array( $poVal );
2160 $checkpoint02 = is_array( $p2[ $poKey ] );
2161 if ( $checkpoint01 && $checkpoint02 ) {
2162 if ( ! nitropack_compare_posts( $poVal, $p2[ $poKey ], $ignoredKeys, 'Re:' ) ) { //'Re:' left for debug purpose to destinguish between main and recursive call
2163 return false;
2164 }
2165 } elseif ( ! $checkpoint01 && ! $checkpoint02 ) {
2166 if ( $poVal != $p2[ $poKey ] ) {
2167 return false;
2168 }
2169 } else {
2170 return false;
2171 }
2172 }
2173 return true;
2174 }
2175
2176 function nitropack_filter_tag( $tag ) {
2177 return preg_replace( "/[^a-zA-Z0-9:]/", ":", $tag );
2178 }
2179
2180 function nitropack_log_tags() {
2181 if ( ! empty( $GLOBALS["NitroPack.instance"] ) && ! empty( $GLOBALS["NitroPack.tags"] ) ) {
2182 $nitro = $GLOBALS["NitroPack.instance"];
2183 $layout = nitropack_get_layout();
2184 try {
2185 $config = $nitro->getConfig();
2186 $useHeader = ! empty( $config->TagsViaHeader );
2187
2188 if ( $layout == "home" ) {
2189 if ( $useHeader ) {
2190 nitropack_header( "x-nitro-tags:pageType:home" );
2191 } else {
2192 $nitro->getApi()->tagUrl( $nitro->getUrl(), "pageType:home" );
2193 }
2194 } else if ( $layout == "archive" ) {
2195 if ( $useHeader ) {
2196 nitropack_header( "x-nitro-tags:pageType:archive" );
2197 } else {
2198 $nitro->getApi()->tagUrl( $nitro->getUrl(), "pageType:archive" );
2199 }
2200 } else {
2201 if ( $useHeader && count( $GLOBALS["NitroPack.tags"] ) <= 100 ) {
2202 nitropack_header( "x-nitro-tags:" . implode( "|", array_map( "nitropack_filter_tag", array_keys( $GLOBALS["NitroPack.tags"] ) ) ) );
2203 } else {
2204 $nitro->getApi()->tagUrl( $nitro->getUrl(), array_map( "nitropack_filter_tag", array_keys( $GLOBALS["NitroPack.tags"] ) ) );
2205 }
2206 }
2207 } catch (\Exception $e) {
2208 }
2209 }
2210 }
2211
2212 function nitropack_extend_nonce_life( $life ) {
2213 // Nonce life should be extended only:
2214 // - if NitroPack is connected for this site
2215 // - if the current value is shorter than the life time of a cache file
2216 // - if no user is logged in
2217 // - for cacheable requests
2218 //
2219 // Reasons why we might need to extend the nonce life time even for requests that are not cacheable:
2220 // - a request may be cachable at first, but become uncachable during changes at runtime or user actions on the page (example: log in via AJAX on a category page. Once logged in the page will not redirect, but if there is an infinite scroll it will stop working if we stop extending the nonce life time)
2221 // - a request may seem cachable at first, but be determined uncachable during runtime (example: visit to a URL of a page whose post type does not match the enabled cacheable post types, or a cart, checkout page, etc.)
2222
2223 if ( ( null !== $nitro = get_nitropack_sdk() ) ) {
2224 $siteConfig = nitropack_get_site_config();
2225 if ( $siteConfig && ! empty( $siteConfig["isDlmActive"] ) && ! empty( $siteConfig["dlm_downloading_url"] ) && ! empty( $siteConfig["dlm_download_endpoint"] ) ) {
2226 $currentUrl = $nitro->getUrl();
2227 if ( strpos( $currentUrl, $siteConfig["dlm_downloading_url"] ) !== false || strpos( $currentUrl, $siteConfig["dlm_download_endpoint"] ) !== false ) {
2228 // Do not modify the nonce times on pages of Download Monitor
2229 return $life;
2230 }
2231 }
2232 $cacheExpiration = $nitro->getConfig()->PageCache->ExpireTime;
2233 return $cacheExpiration > $life ? $cacheExpiration : $life; // Extend the life of cacheable nonces up to the cache expiration time if needed
2234 }
2235 return $life;
2236 }
2237
2238 function nitropack_reconfigure_webhooks() {
2239 nitropack_verify_ajax_nonce( $_REQUEST );
2240 $siteConfig = nitropack_get_site_config();
2241
2242 if ( $siteConfig && ! empty( $siteConfig["siteId"] ) ) {
2243 $siteId = $siteConfig["siteId"];
2244 if ( null !== $nitro = get_nitropack_sdk() ) {
2245 $token = nitropack_generate_webhook_token( $siteId );
2246 try {
2247 nitropack_setup_webhooks( $nitro, $token );
2248 update_option( "nitropack-webhookToken", $token );
2249 nitropack_json_and_exit( array( "status" => "success", 'message' => __( 'Connection reconfigured successfully', 'nitropack' ) ) );
2250 } catch (\NitroPack\SDK\WebhookException $e) {
2251 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Webhook Error: ' . $e );
2252 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Webhook Error: ', 'nitropack' ) . $e->getTraceAsString() ) );
2253 }
2254 } else {
2255 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Unable to get SDK instance' );
2256 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Unable to get SDK instance', 'nitropack' ) ) );
2257 }
2258 } else {
2259 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Incomplete site config. Please reinstall the plugin' );
2260 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Incomplete site config. Please reinstall the plugin!', 'nitropack' ) ) );
2261 }
2262 }
2263
2264 function nitropack_generate_webhook_token( $siteId ) {
2265 return md5( __FILE__ . ":" . $siteId );
2266 }
2267
2268 function nitropack_verify_connect_ajax() {
2269 nitropack_verify_ajax_nonce( $_REQUEST );
2270 $siteId = ! empty( $_POST["siteId"] ) ? $_POST["siteId"] : "";
2271 $siteSecret = ! empty( $_POST["siteSecret"] ) ? $_POST["siteSecret"] : "";
2272 nitropack_verify_connect( $siteId, $siteSecret );
2273 }
2274
2275 function nitropack_check_func_availability( $func_name ) {
2276 if ( function_exists( 'ini_get' ) ) {
2277 $existsResult = stripos( ini_get( 'disable_functions' ), $func_name ) === false;
2278 } else {
2279 $existsResult = function_exists( $func_name );
2280 }
2281 return $existsResult;
2282 }
2283
2284 function nitropack_prevent_connecting( $nitroSDK ) {
2285 $remoteUrl = $nitroSDK->getApi()->getWebhook( "config" );
2286 if ( empty( $remoteUrl ) ) {
2287 return false;
2288 }
2289 $siteConfig = nitropack_get_site_config();
2290 $localUrl = new \NitroPack\Url\Url( $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url() );
2291 $localHome = strtolower( $localUrl->getHost() . $localUrl->getPath() );
2292 $storedUrl = new \NitroPack\Url\Url( $remoteUrl );
2293 $remoteHome = strtolower( $storedUrl->getHost() . $storedUrl->getPath() );
2294 if ( $localHome === $remoteHome ) {
2295 return false;
2296 }
2297 return array( 'local' => $localHome, 'remote' => $remoteHome );
2298 }
2299
2300 function nitropack_verify_connect( $siteId, $siteSecret ) {
2301
2302 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'Verifying connection to NitroPack API' );
2303
2304 if ( ! nitropack_check_func_availability( 'stream_socket_client' ) ) {
2305
2306 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'stream_socket_client function is not allowed by your host.' );
2307
2308 nitropack_json_and_exit( array( "status" => "error", "message" => "stream_socket_client function is not allowed by your host. <a href=\"https://support.nitropack.io/hc/en-us/articles/360020898137\" target=\"_blank\" rel=\"noreferrer noopener\">Read more</a>" ) );
2309 }
2310
2311 if ( ! nitropack_check_func_availability( 'stream_context_create' ) ) {
2312 // <a href=\"https://support.nitropack.io/hc/en-us/articles/360020898137\" target=\"_blank\" rel=\"noreferrer noopener\">Read more</a>
2313 // ^ Similar article needed on website for stream_context_create function
2314 nitropack_json_and_exit( array( "status" => "error", "message" => "stream_context_create function is not allowed by your host." ) );
2315 }
2316
2317 if ( empty( $siteId ) || empty( $siteSecret ) ) {
2318
2319 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Invalid API key or API secret key value' );
2320
2321 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Invalid API key or API secret key value', 'nitropack' ) ) );
2322 }
2323
2324 //remove tags and whitespaces
2325 $siteId = trim( esc_attr( $siteId ) );
2326 $siteSecret = trim( esc_attr( $siteSecret ) );
2327
2328 if ( ! nitropack_validate_site_id( $siteId ) || ! nitropack_validate_site_secret( $siteSecret ) ) {
2329
2330 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Invalid API key or API secret key value' );
2331
2332 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Invalid API key or API secret key value', 'nitropack' ) ) );
2333 }
2334
2335 try {
2336 $blogId = get_current_blog_id();
2337 if ( null !== $nitro = get_nitropack_sdk( $siteId, $siteSecret, NULL, true ) ) {
2338 if ( ! $nitro->checkHealthStatus() ) {
2339
2340 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Error when trying to communicate with NitroPack\'s servers. Current health status: ' . $nitro->getHealthStatus() );
2341
2342 nitropack_json_and_exit( array(
2343 "status" => "error",
2344 "message" => __( 'Error when trying to communicate with NitroPack\'s servers. Please try again in a few minutes. If the issue persists, please', 'nitropack' ) . " <a href='https://support." . NITROPACK_HOST . "/hc/en-us' target='_blank'>contact us</a>."
2345 ) );
2346 }
2347
2348 $preventParing = apply_filters( 'nitropack_prevent_connect', nitropack_prevent_connecting( $nitro ) );
2349 if ( $preventParing ) {
2350
2351 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'It looks like another site ' . $preventParing['remote'] . ' is already connected using these credentials. Either disconnect it or register a new site in your NitroPack dashboard.' );
2352
2353 nitropack_json_and_exit( array(
2354 "status" => "error",
2355 "message" => "It looks like another site <strong>({$preventParing['remote']})</strong> is already connected using these credentials. Either disconnect it or register a new site in your NitroPack dashboard.<br/>
2356 <a href='https://support.nitropack.io/hc/en-us/articles/4405254569745' target='_blank' rel='noreferrer noopener'>Read more</a>"
2357 ) );
2358 }
2359 $token = nitropack_generate_webhook_token( $siteId );
2360 get_nitropack()->settings->set_required_settings( $token );
2361
2362 nitropack_setup_webhooks( $nitro, $token );
2363
2364 // _icl_current_language is WPML cookie, it is added here for compatibility with this module
2365 $customVariationCookies = array( "np_wc_currency", "np_wc_currency_language", "_icl_current_language" );
2366 $variationCookies = $nitro->getApi()->getVariationCookies();
2367 foreach ( $variationCookies as $cookie ) {
2368 $index = array_search( $cookie["name"], $customVariationCookies );
2369 if ( $index !== false ) {
2370 array_splice( $customVariationCookies, $index, 1 );
2371 }
2372 }
2373
2374 foreach ( $customVariationCookies as $cookieName ) {
2375 $nitro->getApi()->setVariationCookie( $cookieName );
2376 }
2377
2378 $nitro->fetchConfig(); // Reload the variation cookies
2379
2380 get_nitropack()->updateCurrentBlogConfig( $siteId, $siteSecret, $blogId );
2381 nitropack_install_advanced_cache();
2382
2383 try {
2384 do_action( 'nitropack_integration_purge_all' );
2385 } catch (\Exception $e) {
2386 // Exception while signaling our 3rd party integration addons to purge their cache
2387 }
2388
2389 nitropack_event( "connect", $nitro );
2390 nitropack_event( "enable_extension", $nitro );
2391
2392 // Optimize front page
2393 $siteConfig = nitropack_get_site_config();
2394 if ( $siteConfig ) {
2395 $nitro->getApi()->runWarmup( [ $siteConfig['home_url'] ], true ); // force run a warmup on the home page
2396 }
2397
2398 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'NitroPack connected' );
2399
2400 $onboarding = get_option( 'nitropack-onboardingPassed' );
2401 $url = $onboarding === '1' ? get_admin_url( $blogId, "admin.php?page=nitropack" ) : get_admin_url( $blogId, "admin.php?page=nitropack&onboarding=1" );
2402 nitropack_json_and_exit( array(
2403 "status" => "success",
2404 "url" => $url,
2405 "message" => __( "Connected", "nitropack" )
2406 ) );
2407 }
2408 } catch (\NitroPack\SDK\WebhookException $e) {
2409
2410 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( $e );
2411
2412 nitropack_json_and_exit( array( "status" => "error", "message" => $e ) );
2413 } catch (\NitroPack\SDK\StorageException $e) {
2414
2415 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Permission Error: ' . $e );
2416
2417 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Permission Error: ', 'nitropack' ) . $e ) );
2418 } catch (\NitroPack\SDK\EmptyConfigException $e) {
2419
2420 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Error while fetching remote config: ' . $e );
2421
2422 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Error while fetching remote config: ', 'nitropack' ) . $e ) );
2423 } catch (\NitroPack\SocketOpenException $e) {
2424
2425 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Can\'t establish connection with NitroPack\'s servers. ' . $e );
2426
2427 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Can\'t establish connection with NitroPack\'s servers', 'nitropack' ) ) );
2428 } catch (\Exception $e) {
2429
2430 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Incorrect API credentials. Please make sure that you copied them correctly and try again. ' . $e );
2431
2432 nitropack_json_and_exit( array( "status" => "error", "message" => __( 'Incorrect API credentials. Please make sure that you copied them correctly and try again.', 'nitropack' ) ) );
2433 }
2434
2435 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'Error verifying connection to NitroPack.' );
2436
2437 nitropack_json_and_exit( array( "status" => "error" ) );
2438 }
2439
2440 function nitropack_reset_webhooks( $nitroSDK ) {
2441 $nitroSDK->getApi()->unsetWebhook( "config" );
2442 $nitroSDK->getApi()->unsetWebhook( "cache_clear" );
2443 $nitroSDK->getApi()->unsetWebhook( "cache_ready" );
2444 }
2445
2446 function nitropack_setup_webhooks( $nitro, $token = NULL ) {
2447 if ( ! $nitro || ! $token ) {
2448 throw new \NitroPack\SDK\WebhookException( 'Webhook token cannot be empty.' );
2449 }
2450
2451 $homeUrl = strtolower( get_home_url() );
2452 $configUrl = new \NitroPack\Url\Url( $homeUrl . "?nitroWebhook=config&token=$token" );
2453 $cacheClearUrl = new \NitroPack\Url\Url( $homeUrl . "?nitroWebhook=cache_clear&token=$token" );
2454 $cacheReadyUrl = new \NitroPack\Url\Url( $homeUrl . "?nitroWebhook=cache_ready&token=$token" );
2455
2456 $nitro->getApi()->setWebhook( "config", $configUrl );
2457 $nitro->getApi()->setWebhook( "cache_clear", $cacheClearUrl );
2458 $nitro->getApi()->setWebhook( "cache_ready", $cacheReadyUrl );
2459 }
2460
2461 function nitropack_disconnect() {
2462 nitropack_verify_ajax_nonce( $_REQUEST );
2463
2464 nitropack_uninstall_advanced_cache();
2465
2466 try {
2467 nitropack_event( "disconnect" );
2468 if ( null !== $nitro = get_nitropack_sdk() ) {
2469 nitropack_reset_webhooks( $nitro );
2470 }
2471 } catch (\Exception $e) {
2472 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->error( 'NitroPack cannot be disconnected. Error: ' . $e );
2473 nitropack_json_and_exit( array( "status" => "error", "message" => $e ) );
2474 }
2475
2476 get_nitropack()->unsetCurrentBlogConfig();
2477
2478 $hostingNoticeFile = nitropack_get_hosting_notice_file();
2479 if ( file_exists( $hostingNoticeFile ) ) {
2480 if ( WP_DEBUG ) {
2481 unlink( $hostingNoticeFile );
2482 } else {
2483 @unlink( $hostingNoticeFile );
2484 }
2485 }
2486 NitroPack\WordPress\NitroPack::getInstance()->getLogger()->notice( 'NitroPack disconnected' );
2487 nitropack_json_and_exit( array( "status" => "success", "message" => __( "Disconnected", "nitropack" ) ) );
2488 }
2489
2490 function nitropack_is_cart_cache_active() {
2491 $nitro = get_nitropack()->getSdk();
2492 if ( $nitro ) {
2493 $config = $nitro->getConfig();
2494 if ( ! empty( $config->StatefulCache->Status ) && ! empty( $config->StatefulCache->CartCache ) ) {
2495 return nitropack_is_cart_cache_available();
2496 }
2497 }
2498 return false;
2499 }
2500
2501 function nitropack_is_cart_cache_available() {
2502 $nitro = get_nitropack()->getSdk();
2503 if ( $nitro ) {
2504 $config = $nitro->getConfig();
2505 if ( ! empty( $config->StatefulCache->isCartCacheAvailable ) ) {
2506 return true;
2507 }
2508 }
2509 return false;
2510 }
2511
2512 function nitropack_handle_compression_toggle( $old_value, $new_value ) {
2513 nitropack_update_blog_compression( $new_value == 1 );
2514 }
2515
2516 function nitropack_update_blog_compression( $enableCompression = false ) {
2517 if ( get_nitropack()->isConnected() ) {
2518 $siteConfig = nitropack_get_site_config();
2519 $siteId = $siteConfig["siteId"];
2520 $siteSecret = $siteConfig["siteSecret"];
2521 $blogId = get_current_blog_id();
2522 get_nitropack()->updateCurrentBlogConfig( $siteId, $siteSecret, $blogId, $enableCompression );
2523 }
2524 }
2525
2526
2527
2528
2529
2530 function nitropack_get_site_config() {
2531 return get_nitropack()->getSiteConfig();
2532 }
2533
2534 function nitropack_get_current_site_id() {
2535
2536 $site_config = nitropack_get_site_config();
2537
2538 if ( $site_config && isset( $site_config['siteId'] ) ) {
2539 return $site_config['siteId'];
2540 }
2541 }
2542
2543 function get_nitropack() {
2544 return \NitroPack\WordPress\NitroPack::getInstance();
2545 }
2546
2547 function nitropack_event( $event, $nitro = null, $additional_meta_data = null ) {
2548 global $wp_version;
2549
2550 try {
2551 $eventUrl = get_nitropack_integration_url( "extensionEvent", $nitro );
2552 $domain = ! empty( $_SERVER["HTTP_HOST"] ) ? $_SERVER["HTTP_HOST"] : "Unknown";
2553
2554
2555 if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
2556 $platform = 'WooCommerce';
2557 } else {
2558 $platform = 'WordPress';
2559 }
2560
2561 $query_data = array(
2562 'event' => $event,
2563 'platform' => $platform,
2564 'platform_version' => $wp_version,
2565 'nitropack_extension_version' => NITROPACK_VERSION,
2566 'additional_meta_data' => $additional_meta_data ? json_encode( $additional_meta_data ) : "{}",
2567 'domain' => $domain
2568 );
2569
2570 $client = new NitroPack\HttpClient\HttpClient( $eventUrl . '&' . http_build_query( $query_data ) );
2571 $client->doNotDownload = true;
2572 $client->fetch();
2573 } catch (\Exception $e) {
2574 }
2575 }
2576
2577 function nitropack_get_wpconfig_path() {
2578 $configFilePath = nitropack_trailingslashit( ABSPATH ) . "wp-config.php";
2579 if ( ! file_exists( $configFilePath ) ) {
2580 $configFilePath = nitropack_trailingslashit( dirname( ABSPATH ) ) . "wp-config.php";
2581 $settingsFilePath = nitropack_trailingslashit( dirname( ABSPATH ) ) . "wp-settings.php"; // We need to check for this file to avoid confusion if the current installation is a nested directory of another WP installation. Refer to wp-load.php for more information.
2582 if ( ! file_exists( $configFilePath ) || file_exists( $settingsFilePath ) ) {
2583 return false;
2584 }
2585 }
2586
2587
2588 if ( ! is_writable( $configFilePath ) ) {
2589 return false;
2590 }
2591
2592 return $configFilePath;
2593 }
2594
2595 function nitropack_get_htaccess_path() {
2596 $configFilePath = nitropack_trailingslashit( ABSPATH ) . ".htaccess";
2597 if ( ! file_exists( $configFilePath ) ) {
2598 return false;
2599 }
2600
2601
2602 if ( ! is_writable( $configFilePath ) ) {
2603 return false;
2604 }
2605
2606 return $configFilePath;
2607 }
2608
2609 function nitropack_detect_hosting() {
2610 if ( \NitroPack\Integration\Hosting\Flywheel::detect() ) {
2611 return "flywheel";
2612 } else if ( \NitroPack\Integration\Hosting\Cloudways::detect() ) {
2613 return "cloudways";
2614 } else if ( \NitroPack\Integration\Hosting\WPEngine::detect() ) {
2615 return "wpengine";
2616 } else if ( \NitroPack\Integration\Hosting\SiteGround::detect() ) {
2617 return "siteground";
2618 } else if ( \NitroPack\Integration\Hosting\GoDaddyWPaaS::detect() ) {
2619 return "godaddy_wpaas";
2620 } else if ( \NitroPack\Integration\Hosting\GridPane::detect() ) {
2621 return "gridpane";
2622 } else if ( \NitroPack\Integration\Hosting\Kinsta::detect() ) {
2623 return "kinsta";
2624 } else if ( \NitroPack\Integration\Hosting\Closte::detect() ) {
2625 return "closte";
2626 } else if ( \NitroPack\Integration\Hosting\Pagely::detect() ) {
2627 return "pagely";
2628 } else if ( \NitroPack\Integration\Hosting\WPX::detect() ) {
2629 return "wpx";
2630 } else if ( \NitroPack\Integration\Hosting\Vimexx::detect() ) {
2631 return "vimexx";
2632 } else if ( \NitroPack\Integration\Hosting\Pressable::detect() ) {
2633 return "pressable";
2634 } else if ( \NitroPack\Integration\Hosting\RocketNet::detect() ) {
2635 return "rocketnet";
2636 } else if ( \NitroPack\Integration\Hosting\Savvii::detect() ) {
2637 return "savvii";
2638 } else if ( \NitroPack\Integration\Hosting\DreamHost::detect() ) {
2639 return "dreamhost";
2640 } else if ( \NitroPack\Integration\Hosting\Raidboxes::detect() ) {
2641 return "raidboxes";
2642 } else {
2643 return "unknown";
2644 }
2645 }
2646
2647 function nitropack_removeCacheBustParam( $content ) {
2648 $content = preg_replace( "/(\?|%26|&#0?38;|&#x0?26;|&(amp;)?)ignorenitro(%3D|=)[a-fA-F0-9]{32}(?!%26|&#0?38;|&#x0?26;|&(amp;)?)\/?/mu", "", $content );
2649 return preg_replace( "/(\?|%26|&#0?38;|&#x0?26;|&(amp;)?)ignorenitro(%3D|=)[a-fA-F0-9]{32}(%26|&#0?38;|&#x0?26;|&(amp;)?)/mu", "$1", $content );
2650 }
2651
2652 function nitropack_handle_request( $servedFrom = "unknown" ) {
2653
2654 global $np_integrationSetupEvent;
2655
2656 if ( isset( $_GET["ignorenitro"] ) ) {
2657 unset( $_GET["ignorenitro"] );
2658 }
2659
2660 if ( defined( "NITROPACK_STRIP_IGNORENITRO" ) && NITROPACK_STRIP_IGNORENITRO && $_SERVER['REQUEST_URI'] != '' ) {
2661 $_SERVER['REQUEST_URI'] = nitropack_removeCacheBustParam( $_SERVER['REQUEST_URI'] );
2662 }
2663
2664 nitropack_header( 'Cache-Control: no-cache' );
2665 do_action( "nitropack_early_cache_headers" ); // Overrides the Cache-Control header on supported platforms
2666 $isManageWpRequest = ! empty( $_GET["mwprid"] );
2667 $isWpCli = nitropack_is_wp_cli();
2668
2669 if ( file_exists( NITROPACK_CONFIG_FILE ) && ! empty( $_SERVER["HTTP_HOST"] ) && ! empty( $_SERVER["REQUEST_URI"] ) && ! $isManageWpRequest && ! $isWpCli ) {
2670 try {
2671 $siteConfig = nitropack_get_site_config();
2672 if ( $siteConfig && null !== $nitro = get_nitropack_sdk( $siteConfig["siteId"], $siteConfig["siteSecret"] ) ) {
2673 if ( is_valid_nitropack_webhook() ) {
2674 nitropack_handle_webhook();
2675 } else if ( is_valid_nitropack_beacon() ) {
2676 nitropack_handle_beacon();
2677 } else if ( is_valid_nitropack_heartbeat() ) {
2678 nitropack_handle_heartbeat();
2679 } else {
2680 $GLOBALS["NitroPack.instance"] = $nitro;
2681
2682 if ( nitropack_passes_cookie_requirements() || ( nitropack_is_ajax() && ! empty( $_COOKIE["nitroCachedPage"] ) ) ) {
2683 // Check whether the current URL is cacheable
2684 // If this is an AJAX request, check whether the referer is cachable - this is needed for cases when NitroPack's "Enabled URLs" option is being used to whitelist certain URLs.
2685 // If we are not checking the referer, the AJAX requests on these pages can fail.
2686 $urlToCheck = nitropack_is_ajax() && ! empty( $_SERVER["HTTP_REFERER"] ) ? $_SERVER["HTTP_REFERER"] : $nitro->getUrl();
2687 if ( $nitro->isAllowedUrl( $urlToCheck ) ) {
2688 add_filter( 'nonce_life', 'nitropack_extend_nonce_life' );
2689 }
2690 }
2691
2692 if ( nitropack_passes_cookie_requirements() && apply_filters( "nitropack_can_serve_cache", true ) ) {
2693 if ( $nitro->isCacheAllowed() ) {
2694 if ( ! nitropack_is_ajax() ) {
2695 do_action( "nitropack_cacheable_cache_headers" );
2696 }
2697
2698 if ( ! empty( $siteConfig["compression"] ) ) {
2699 $nitro->enableCompression();
2700 }
2701
2702 if ( $nitro->hasLocalCache() ) {
2703 // TODO: Make this work so we can provide the reverse proxies with this information $remainingTtl = $nitr->pageCache->getRemainingTtl();
2704 do_action( "nitropack_cachehit_cache_headers" ); // TODO: Pass the remaining TTL here
2705 $cacheControlOverride = defined( "NITROPACK_CACHE_CONTROL_OVERRIDE" ) ? NITROPACK_CACHE_CONTROL_OVERRIDE : NULL;
2706 if ( $cacheControlOverride ) {
2707 nitropack_header( 'Cache-Control: ' . $cacheControlOverride );
2708 }
2709
2710 nitropack_header( 'X-Nitro-Cache: HIT' );
2711 nitropack_header( 'X-Nitro-Cache-From: ' . $servedFrom );
2712 $cjHandler = new \NitroPack\SDK\Utils\CjHandler( $nitro );
2713 $cjHandler->handleQueryParams();
2714 $nitro->pageCache->readfile();
2715 exit;
2716 } else {
2717 // We need the following if..else block to handle bot requests which will not be firing our beacon
2718 if ( nitropack_is_warmup_request() ) {
2719 if ( ! empty( $_SERVER["HTTP_ACCEPT_LANGUAGE"] ) ) {
2720 add_action( "init", function () use ($nitro) {
2721 $nitro->hasRemoteCache( "default" ); // Only ping the API letting our service know that this page must be cached.
2722 exit;
2723 }, 9999 );
2724 return; // We need to wait for a language plugin (if present) to redirect
2725 } else {
2726 $nitro->hasRemoteCache( "default" ); // Only ping the API letting our service know that this page must be cached.
2727 exit; // No need to continue handling this request. The response is not important.
2728 }
2729 } else if ( nitropack_is_lighthouse_request() || nitropack_is_gtmetrix_request() || nitropack_is_pingdom_request() ) {
2730 $nitro->hasRemoteCache( "default" ); // Ping the API letting our service know that this page must be cached.
2731 }
2732
2733 $nitro->pageCache->useInvalidated( true );
2734 if ( $nitro->hasLocalCache() ) {
2735 nitropack_header( 'X-Nitro-Cache: STALE' );
2736 nitropack_header( 'X-Nitro-Cache-From: ' . $servedFrom );
2737 $cjHandler = new \NitroPack\SDK\Utils\CjHandler( $nitro );
2738 $cjHandler->handleQueryParams();
2739 $nitro->pageCache->readfile();
2740 exit;
2741 } else {
2742 $nitro->pageCache->useInvalidated( false );
2743 }
2744 }
2745 }
2746 }
2747 }
2748 }
2749 } catch (\Exception $e) {
2750 // Do nothing, cache serving will be handled by nitropack_init
2751 }
2752 }
2753 }
2754
2755 function nitropack_is_dropin_cache_allowed() {
2756 $siteConfig = nitropack_get_site_config();
2757 return $siteConfig && empty( $siteConfig["isEzoicActive"] );
2758 }
2759
2760
2761
2762
2763
2764 function nitropack_cookiepath() {
2765 $siteConfig = nitropack_get_site_config();
2766 $homeUrl = $siteConfig && ! empty( $siteConfig["home_url"] ) ? $siteConfig["home_url"] : get_home_url();
2767 $url = new \NitroPack\Url\Url( $homeUrl );
2768 return $url ? $url->getPath() : "/";
2769 }
2770
2771 function nitropack_cookie_path_ajax() {
2772 nitropack_verify_ajax_nonce( $_REQUEST );
2773 nitropack_json_and_exit( array(
2774 'cookie_path' => nitropack_cookiepath()
2775 ) );
2776 }
2777 add_action( 'wp_ajax_nitropack_cookie_path_ajax', 'nitropack_cookie_path_ajax' );
2778
2779 function nitropack_setcookie( $name, $value, $expires = NULL, $options = [] ) {
2780 if ( headers_sent() )
2781 return;
2782 $cookie_options = '';
2783 $cookie_path = nitropack_cookiepath();
2784
2785 if ( $expires && is_numeric( $expires ) ) {
2786 $options["Expires"] = date( "D, d M Y H:i:s", (int) $expires ) . ' GMT';
2787 }
2788
2789 if ( empty( $options["SameSite"] ) ) {
2790 $options["SameSite"] = "Lax";
2791 }
2792
2793 foreach ( $options as $optName => $optValue ) {
2794 $cookie_options .= "$optName=$optValue; ";
2795 }
2796 nitropack_header( "set-cookie: $name=$value; Path=$cookie_path; " . $cookie_options, false );
2797 }
2798
2799 function nitropack_header( $header, $replace = true, $response_code = 0 ) {
2800 if ( ! nitropack_is_wp_cron() && ! nitropack_is_wp_cli() ) {
2801 header( $header, $replace, $response_code );
2802 }
2803 }
2804
2805 function nitropack_upgrade_handler( $entity ) {
2806 $np = 'nitropack/main.php';
2807 $trigger = $entity;
2808 if ( $entity instanceof Plugin_Upgrader ) {
2809 $trigger = $entity->plugin_info();
2810 if ( ! is_plugin_active( $trigger ) ) {
2811 return;
2812 }
2813 }
2814
2815 if ( $entity instanceof Theme_Upgrader ) {
2816 if ( $entity->theme_info()->Name === wp_get_theme()->Name ) {
2817 nitropack_theme_handler( 'Theme updated' );
2818 }
2819 return;
2820 }
2821
2822 if ( $trigger !== $np ) {
2823 $cookie_expires = date( "D, d M Y H:i:s", time() + 600 ) . ' GMT';
2824 nitropack_setcookie( 'nitropack_apwarning', "1", time() + 600 );
2825 }
2826 }
2827 /**
2828 * Caches some options in the config so that we can access them before get_option() is defined
2829 * which is in advanced_cache.php, functions.php and Integrations
2830 */
2831 function nitropack_updated_option( $option, $oldValue, $value ) {
2832 $neededOptions = \NitroPack\WordPress\NitroPack::$optionsToCache;
2833 if ( ! in_array( $option, $neededOptions ) )
2834 return;
2835
2836 $np = get_nitropack();
2837 $siteConfig = $np->Config->get();
2838
2839 if ( function_exists( 'get_home_url' ) ) {
2840 $configKey = \NitroPack\WordPress\NitroPack::getConfigKey();
2841 $siteConfig[ $configKey ]['options_cache'][ $option ] = $value;
2842 $np->Config->set( $siteConfig );
2843 }
2844 }
2845
2846 function nitropack_is_late_integration_init_required() {
2847 return \NitroPack\Integration\Plugin\NginxHelper::isActive() || \NitroPack\Integration\Plugin\Cloudflare::isApoActive();
2848 }
2849
2850 function nitropack_get_notice_id( $message ) {
2851 return md5( $message );
2852 }
2853
2854 function nitropack_active_sitemap_plugins() {
2855 return
2856 NitroPack\Integration\Plugin\YoastSEO::isActive() ||
2857 NitroPack\Integration\Plugin\JetPackNP::isActive() ||
2858 NitroPack\Integration\Plugin\SquirrlySEO::isActive() ||
2859 NitroPack\Integration\Plugin\RankMathNP::isActive();
2860 }
2861
2862 function nitropack_get_site_maps() {
2863 $sitemapUrls['YoastSEO'] = NitroPack\Integration\Plugin\YoastSEO::getSitemapURL();
2864 $sitemapUrls['JetPack'] = NitroPack\Integration\Plugin\JetPackNP::getSitemapURL();
2865 $sitemapUrls['SquirrlySEO'] = NitroPack\Integration\Plugin\SquirrlySEO::getSitemapURL();
2866 $sitemapUrls['RankMath'] = NitroPack\Integration\Plugin\RankMathNP::getSitemapURL();
2867
2868 return $sitemapUrls;
2869 }
2870
2871 function get_default_sitemap() {
2872
2873 $defaultSiteMap = NitroPack\Integration\Plugin\WPCacheHelper::getSitemapURL();
2874 if ( $defaultSiteMap ) {
2875 set_sitemap_indication_msg( 'WordPress', $defaultSiteMap );
2876 return $defaultSiteMap;
2877 }
2878
2879 return false;
2880 }
2881
2882 function evaluate_warmup_sitemap( $sitemapUrls ) {
2883
2884 $sitemapProviders = array(
2885 'YoastSEO' => 'Yoast!',
2886 'SquirrlySEO' => 'Squirrly SEO',
2887 'RankMath' => 'Rank Math',
2888 'JetPack' => 'Jetpack',
2889 );
2890
2891 foreach ( $sitemapProviders as $provider => $name ) {
2892 if ( isset( $sitemapUrls[ $provider ] ) && $sitemapUrls[ $provider ] ) {
2893 set_sitemap_indication_msg( $name, $sitemapUrls[ $provider ] );
2894 return $sitemapUrls[ $provider ];
2895 }
2896 }
2897
2898 return get_default_sitemap();
2899 }
2900
2901 function set_sitemap_indication_msg( $pluginName, $sitemapURL ) {
2902 $sitemapURI = explode( "/", parse_url( $sitemapURL, PHP_URL_PATH ) );
2903 $msg = $sitemapURI[1] . ' used by ' . $pluginName;
2904 update_option( 'nitropack-warmup-sitemap', $msg );
2905 }
2906
2907 function get_date_midpoint( $endDate ) {
2908 return ( time() + strtotime( $endDate ) ) / 2;
2909 }
2910
2911
2912 function initVariationCookies( $customVariationCookies ) {
2913 $api = get_nitropack_sdk()->getApi();
2914 try {
2915 $variationCookies = $api->getVariationCookies();
2916 foreach ( $variationCookies as $cookie ) {
2917 $index = array_search( $cookie["name"], $customVariationCookies );
2918 if ( $index !== false ) {
2919 array_splice( $customVariationCookies, $index, 1 );
2920 }
2921 }
2922
2923 foreach ( $customVariationCookies as $cookieName ) {
2924 $api->setVariationCookie( $cookieName );
2925 }
2926 } catch (\Exception $e) {
2927 // what to do here? possible reason for exception is the API not responding
2928 return false;
2929 }
2930 }
2931
2932 function removeVariationCookies( $cookiesToRemove ) {
2933 $api = get_nitropack_sdk()->getApi();
2934 try {
2935 $variationCookies = $api->getVariationCookies();
2936 foreach ( $variationCookies as $cookie ) {
2937 if ( in_array( $cookie["name"], $cookiesToRemove ) ) {
2938 $api->unsetVariationCookie( $cookie["name"] );
2939 }
2940 }
2941 } catch (\Exception $e) {
2942 // what to do here? possible reason for exception is the API not responding
2943 return false;
2944 }
2945 }
2946
2947 function getNewCookie( $name ) {
2948 $cookies = getNewCookies();
2949 return ! empty( $cookies[ $name ] ) ? $cookies[ $name ] : null;
2950 }
2951
2952 /**
2953 * Returns an array of newly set cookies (not in $_COOKIE), that will be sent along with the headers of the current response.
2954 */
2955 function getNewCookies() {
2956 $cookies = [];
2957 $headers = headers_list();
2958 foreach ( $headers as $header ) {
2959 if ( strpos( $header, 'Set-Cookie: ' ) === 0 ) {
2960 $value = str_replace( '&', urlencode( '&' ), substr( $header, 12 ) );
2961 parse_str( current( explode( ';', $value ) ), $pair );
2962 $cookies = array_merge_recursive( $cookies, $pair );
2963 }
2964 }
2965
2966 return array_filter( array_map( function ( $val ) {
2967 if ( is_array( $val ) ) {
2968 $lastEl = end( $val );
2969 if ( is_array( $lastEl ) ) {
2970 return NULL;
2971 }
2972 return $lastEl;
2973 }
2974
2975 return $val;
2976 }, $cookies ) );
2977 }
2978
2979 /**
2980 * Purge entire cache when permalink structure is changed.
2981 *
2982 * @param string $old_permalink_structure The previous permalink structure.
2983 * @param string $permalink_structure The new permalink structure.
2984 *
2985 * @return void
2986 */
2987 function nitropack_permalink_structure_changed_handler( $old_permalink_structure, $permalink_structure ) {
2988
2989 if ( $old_permalink_structure != $permalink_structure && get_option( "nitropack-autoCachePurge", 1 ) ) {
2990 $msg = 'The permalink structure is changed. Purging the cache for the home page.';
2991 $url = get_home_url();
2992
2993 try {
2994 nitropack_sdk_purge( $url, NULL, $msg ); // purge cache for the home page
2995 } catch (\Exception $e) {
2996 }
2997
2998 // run warmup
2999 if ( null !== $nitro = get_nitropack_sdk() ) {
3000 try {
3001 $nitro->getApi()->runWarmup();
3002 } catch (\Exception $e) {
3003 }
3004 }
3005 }
3006 }
3007
3008 add_action( 'permalink_structure_changed', 'nitropack_permalink_structure_changed_handler', 10, 2 );
3009
3010 /**
3011 * Purge entire cache when front page is changed.
3012 *
3013 * @param array $old_value An array of previous settings values.
3014 * @param array $value An array of submitted settings values.
3015 *
3016 * @return void
3017 */
3018 function nitropack_frontpage_changed_handler( $old_value, $value ) {
3019
3020 if ( $old_value !== $value ) {
3021 $msg = 'The front page is changed';
3022 $url = get_home_url();
3023
3024 try {
3025 nitropack_sdk_purge( $url, NULL, $msg ); // purge entire cache
3026 } catch (\Exception $e) {
3027 }
3028 }
3029 }
3030
3031 add_action( 'update_option_show_on_front', 'nitropack_frontpage_changed_handler', 10, 2 );
3032 add_action( 'update_option_page_on_front', 'nitropack_frontpage_changed_handler', 10, 2 );
3033 add_action( 'update_option_page_for_posts', 'nitropack_frontpage_changed_handler', 10, 2 );
3034
3035 // Init integration action handlers
3036 $modHandler = NitroPack\ModuleHandler::getInstance();
3037 $modHandler->init();
3038