From fc14784998a752e10b881423b9af4700e4944eda Mon Sep 17 00:00:00 2001 From: Alessio Torrisi Date: Wed, 18 Dec 2024 15:39:33 +0100 Subject: [PATCH] Handle htaccess file according to burst safety mode --- bootstrap.php | 2 + includes/BurstSafetyMode/Browser.php | 59 +++++++++ .../BurstSafetyMode/ResponseHeaderManager.php | 116 ++++++++++++++++++ includes/BurstSafetyMode/Skip404.php | 45 +++++++ includes/BurstSafetyMode/init.php | 61 +++++++++ includes/burstSafetyModeFunctions.php | 44 +++++++ tests/cypress/integration/performance.cy.js | 2 +- 7 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 includes/BurstSafetyMode/Browser.php create mode 100644 includes/BurstSafetyMode/ResponseHeaderManager.php create mode 100644 includes/BurstSafetyMode/Skip404.php create mode 100644 includes/BurstSafetyMode/init.php create mode 100644 includes/burstSafetyModeFunctions.php diff --git a/bootstrap.php b/bootstrap.php index c4d16d8..f5bfb9c 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -12,3 +12,5 @@ function ( $features ) { } new PerformanceFeatureHooks(); + +require_once __DIR__ . '/includes/BurstSafetyMode/init.php'; diff --git a/includes/BurstSafetyMode/Browser.php b/includes/BurstSafetyMode/Browser.php new file mode 100644 index 0000000..9bbba02 --- /dev/null +++ b/includes/BurstSafetyMode/Browser.php @@ -0,0 +1,59 @@ +addHeader( 'X-Newfold-Cache-Level', BURST_SAFETY_CACHE_LEVEL ); + $this->addRules(); + } + + /** + * Add htaccess rules. + * + * @return void + */ + public static function addRules() { + + $file_typ_expirations = array( + 'default' => '1 week', + 'text/html' => '8 hours', + 'image/jpg' => '1 week', + 'image/jpeg' => '1 week', + 'image/gif' => '1 week', + 'image/png' => '1 week', + 'text/css' => '1 week', + 'text/javascript' => '1 week', + 'application/pdf' => '1 month', + 'image/x-icon' => '1 year', + ); + + $tab = "\t"; + + $rules[] = ''; + $rules[] = "{$tab}ExpiresActive On"; + + foreach ( $file_typ_expirations as $file_type => $expiration ) { + if ( 'default' === $file_type ) { + $rules[] = "{$tab}ExpiresDefault \"access plus {$expiration}\""; + } else { + $rules[] = "{$tab}ExpiresByType {$file_type} \"access plus {$expiration}\""; + } + } + + $rules [] = ''; + + $htaccess = new htaccess( self::MARKER ); + + return $htaccess->addContent( $rules ); + } +} diff --git a/includes/BurstSafetyMode/ResponseHeaderManager.php b/includes/BurstSafetyMode/ResponseHeaderManager.php new file mode 100644 index 0000000..3a3365c --- /dev/null +++ b/includes/BurstSafetyMode/ResponseHeaderManager.php @@ -0,0 +1,116 @@ +htaccess = new htaccess( self::MARKER ); + } + + /** + * Parse existing headers. + * + * @return array + */ + public function parseHeaders() { + + $headers = array(); + + $content = $this->htaccess->readContent(); + $lines = array_map( 'trim', convertContentToLines( $content ) ); + + array_shift( $lines ); // Remove opening IfModule + array_pop( $lines ); // Remove closing IfModule + + $pattern = '/^Header set (.*) "(.*)"$/'; + + foreach ( $lines as $line ) { + if ( preg_match( $pattern, trim( $line ), $matches ) && isset( $matches[1], $matches[2] ) ) { + $headers[ $matches[1] ] = $matches[2]; + } + } + + return $headers; + } + + /** + * Add a header. + * + * @param string $name Header name + * @param string $value Header value + */ + public function addHeader( string $name, string $value ) { + $this->setHeaders( + array_merge( + $this->parseHeaders(), + array( $name => $value ) + ) + ); + } + + /** + * Add multiple headers at once. + * + * @param string[] $headers + */ + public function addHeaders( array $headers ) { + $headers = array_merge( $this->parseHeaders(), $headers ); + $this->setHeaders( $headers ); + } + + /** + * Remove a header. + * + * @param string $name Header name + */ + public function removeHeader( $name ) { + $headers = $this->parseHeaders(); + unset( $headers[ $name ] ); + $this->setHeaders( $headers ); + } + + /** + * Remove all headers. + */ + public function removeAllHeaders() { + $this->setHeaders( array() ); + } + + /** + * Set headers. + * + * @param array $headers + */ + public function setHeaders( array $headers ) { + + if ( empty( $headers ) ) { + $this->htaccess->removeContent(); + + return; + } + + $content = '' . PHP_EOL; + foreach ( $headers as $key => $value ) { + $content .= "\t" . "Header set {$key} \"{$value}\"" . PHP_EOL; + } + $content .= ''; + + $this->htaccess->addContent( $content ); + } +} diff --git a/includes/BurstSafetyMode/Skip404.php b/includes/BurstSafetyMode/Skip404.php new file mode 100644 index 0000000..a7e061e --- /dev/null +++ b/includes/BurstSafetyMode/Skip404.php @@ -0,0 +1,45 @@ +addRules(); + } + + + /** + * Add our rules to the .htacces file. + */ + public static function addRules() { + $content = << + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} !(robots\.txt|ads\.txt|[a-z0-9_\-]*sitemap[a-z0-9_\.\-]*\.(xml|xsl|html)(\.gz)?) + RewriteCond %{REQUEST_URI} \.(css|htc|less|js|js2|js3|js4|html|htm|rtf|rtx|txt|xsd|xsl|xml|asf|asx|wax|wmv|wmx|avi|avif|avifs|bmp|class|divx|doc|docx|eot|exe|gif|gz|gzip|ico|jpg|jpeg|jpe|webp|json|mdb|mid|midi|mov|qt|mp3|m4a|mp4|m4v|mpeg|mpg|mpe|webm|mpp|otf|_otf|odb|odc|odf|odg|odp|ods|odt|ogg|ogv|pdf|png|pot|pps|ppt|pptx|ra|ram|svg|svgz|swf|tar|tif|tiff|ttf|ttc|_ttf|wav|wma|wri|woff|woff2|xla|xls|xlsx|xlt|xlw|zip)$ [NC] + RewriteRule .* - [L] + +HTACCESS; + + addContent( self::MARKER, $content ); + } + + /** + * Remove our rules from the .htaccess file. + */ + public static function removeRules() { + removeMarkers( self::MARKER ); + } +} diff --git a/includes/BurstSafetyMode/init.php b/includes/BurstSafetyMode/init.php new file mode 100644 index 0000000..66a3c8e --- /dev/null +++ b/includes/BurstSafetyMode/init.php @@ -0,0 +1,61 @@ +addHeader( 'X-Newfold-Cache-Level', $newfold_cache_level ); + + delete_option( 'newfold_burst_safety_mode' ); + } +} else { + if ( ! $newfold_burst_safety_mode ) { + $files_to_include = array( + 'htaccess' => BLUEHOST_PLUGIN_DIR . 'vendor/wp-forge/wp-htaccess-manager/includes/htaccess.php', + 'htaccess_functions' => BLUEHOST_PLUGIN_DIR . 'vendor/wp-forge/wp-htaccess-manager/includes/functions.php', + 'skip404' => BLUEHOST_PLUGIN_DIR . 'vendor/newfold-labs/wp-module-performance/includes/BurstSafetyMode/Skip404.php', + 'browser' => BLUEHOST_PLUGIN_DIR . 'vendor/newfold-labs/wp-module-performance/includes/BurstSafetyMode/browser.php', + 'response_header_manager' => BLUEHOST_PLUGIN_DIR . 'vendor/newfold-labs/wp-module-performance/includes/BurstSafetyMode/ResponseHeaderManager.php', + ); + + foreach ( $files_to_include as $path ) { + if ( file_exists( $path ) ) { + require_once $path; + } + } + + define( 'BURST_SAFETY_CACHE_LEVEL', 3 ); + + $skip_404_handling = (bool) get_option( 'newfold_skip_404_handling', true ); + + if ( ! $skip_404_handling && class_exists( BurstSkip404::class ) ) { + $skip404 = new BurstSkip404(); + } + + if ( BURST_SAFETY_CACHE_LEVEL !== $newfold_cache_level && class_exists( BurstBrowser::class ) ) { + $browser = new BurstBrowser(); + } + + update_option( 'newfold_burst_safety_mode', true ); + } +} diff --git a/includes/burstSafetyModeFunctions.php b/includes/burstSafetyModeFunctions.php new file mode 100644 index 0000000..8de058d --- /dev/null +++ b/includes/burstSafetyModeFunctions.php @@ -0,0 +1,44 @@ +addHeader( 'X-Newfold-Cache-Level', 3 ); + } +} elseif ( $newfold_burst_safety_mode ) { + $cache_level = get_option( 'newfold_burst_safety_mode_site_cache_level' ); + $browser = new Browser(); + $browser::maybeAddRules( $cache_level ); + if( function_exists( 'getSkip404Option' ) && ! getSkip404Option() ) { + $skip404 = new Skip404(); + $skip404::maybeAddRules( false ); + } + $responseHeaderManager = new ResponseHeaderManager(); + $responseHeaderManager->addHeader( 'X-Newfold-Cache-Level', $cache_level ); + delete_option( 'newfold_burst_safety_mode' ); + delete_option( 'newfold_burst_safety_mode_site_cache_level' ); +} diff --git a/tests/cypress/integration/performance.cy.js b/tests/cypress/integration/performance.cy.js index e12fc15..111f9f2 100644 --- a/tests/cypress/integration/performance.cy.js +++ b/tests/cypress/integration/performance.cy.js @@ -13,7 +13,7 @@ describe( 'Performance Page', function () { } ); it( 'Is Accessible', () => { - cy.wait( 1000 ); + cy.wait( 2000 ); cy.checkA11y( appClass + '-app-body' ); } );