Boost Posted January 25, 2024 Share Posted January 25, 2024 This is my approach to combining CSS files for improved page speed. The implementation is straightforward; you only need to add one PHP file to your server and make a few tweaks. My setup: -site/ -templates/ - inc/ - styles/ _init.php Include the CombinedCSS.php file at the top of my _init.php: include_once('./inc/CombinedCSS.php'); _main.php Add the following to the <head> section: <?php $cssFiles = [ paths()->templates . 'styles/uikit.min.css', paths()->templates . 'styles/uikit.ext.css', paths()->templates . 'styles/main.css' ]; echo CombinedCSS::CSS($cssFiles); ?> CombinedCSS.php <?php namespace ProcessWire; /** * Import the RuntimeException class for throwing runtime exceptions. */ use RuntimeException; /** * Class CombinedCSS * * A class for combining and minifying CSS files and generating a link tag for the combined CSS. */ class CombinedCSS { /** * @var string|null The output directory for saving the combined CSS file. */ private static $outputDirectory; /** * @var string|null Hash of the concatenated content of CSS files. */ private static $filesHash; /** * Initializes the output directory if it is not already set. */ private static function initOutputDirectory() { if (!isset(self::$outputDirectory)) { self::$outputDirectory = paths()->templates . 'styles/'; } } /** * Checks if a directory is writable. * * @param string $directory The directory path to check. * @return bool True if the directory is writable, false otherwise. */ private static function isWritableDirectory($directory) { // Check if the directory exists if (!is_dir($directory)) { return false; } // Check if the directory is writable if (!is_writable($directory)) { return false; } return true; } /** * Combines and minifies an array of CSS files. * * @param array $files An array of CSS file paths. * @return string The combined and minified CSS content. */ private static function combineAndMinifyCSS(array $files): string { $combinedCss = ''; foreach ($files as $file) { // Read the content of each CSS file $cssContent = file_get_contents($file); // Minify the CSS content (you can replace this with your preferred minification logic) $minifiedCss = self::minifyCSS($cssContent); // Append the minified CSS to the combined CSS content $combinedCss .= $minifiedCss; } return $combinedCss; } /** * Minifies CSS content. * * @param string $css The CSS content to be minified. * @return string The minified CSS content. */ private static function minifyCSS(string $css): string { // Replace this with your preferred CSS minification logic // Example: removing comments, extra whitespaces, etc. $minifiedCss = preg_replace('/\/\*.*?\*\//s', '', $css); $minifiedCss = preg_replace('/\s+/', ' ', $minifiedCss); return $minifiedCss; } /** * Generates a unique filename for the combined CSS file based on the provided file paths. * * @param array $files An array of CSS file paths. * @return string The generated combined CSS filename. */ private static function generateCombinedFilename(array $files): string { return md5(implode('', $files)) . '.css'; } /** * Saves the combined CSS content to a file. * * @param string $filename The filename for the combined CSS file. * @param string $content The combined CSS content to be saved. * * @throws RuntimeException If there is an error writing the file to disk. */ private static function saveToFile(string $filename, string $content): void { // Save the combined CSS content to the specified file $filePath = self::$outputDirectory . DIRECTORY_SEPARATOR . $filename; if (file_put_contents($filePath, $content) === false) { $error = error_get_last(); $errorMessage = isset($error['message']) ? $error['message'] : 'Unknown error'; throw new RuntimeException('Failed to write the combined CSS file to disk. Error: ' . $errorMessage); } } /** * Generates a link tag for the combined CSS file based on the provided file paths. * * @param array $files An array of CSS file paths. * @return string The link tag for the combined CSS file. * @throws RuntimeException If the output directory is not writable. */ public static function CSS(array $files): string { // Initialize the output directory self::initOutputDirectory(); // Ensure the output directory is writable if (!self::isWritableDirectory(self::$outputDirectory)) { throw new RuntimeException('The output directory is not writable.'); } // Combine and minify CSS files $combinedCss = self::combineAndMinifyCSS($files); // Check if the files have changed by comparing their hash $currentFilesHash = md5($combinedCss); $combinedFilename = ''; if ($currentFilesHash !== self::$filesHash) { // If the files have changed, generate a unique filename for the combined CSS file $combinedFilename = self::generateCombinedFilename($files); // Save the combined CSS content to a file self::saveToFile($combinedFilename, $combinedCss); // Update the files hash self::$filesHash = $currentFilesHash; } else { // If the files have not changed, check if the file exists on disk $combinedFilename = self::generateCombinedFilename($files); $filePath = self::$outputDirectory . DIRECTORY_SEPARATOR . $combinedFilename; if (!file_exists($filePath)) { // If the file doesn't exist, save the combined CSS content to a file self::saveToFile($combinedFilename, $combinedCss); } } // Return the link tag return '<link rel="stylesheet" href="' . urls()->templates . 'styles/' . $combinedFilename . '">'; } } Notes: Ensure that you have writing permissions for the $outputDirectory location. The combined CSS file will be generated and saved only if the files have changed or if the file doesn't exist on disk. The order of the combined file will adhere to the sequence specified in the array. Modify the hardcoded paths based on your requirements. Any suggestions for improvements are welcome! Cheers! 1 Link to comment Share on other sites More sharing options...
bernhard Posted January 25, 2024 Share Posted January 25, 2024 18 minutes ago, Boost said: Any suggestions for improvements are welcome! 18 minutes ago, Boost said: This is my approach to combining CSS files for improved page speed I don't think that this is true any more with HTTP2 because browsers can download multiple files at once. That's why I didn't implement something like this in RockFrontend. But I'm not sure ? Link to comment Share on other sites More sharing options...
Boost Posted January 25, 2024 Author Share Posted January 25, 2024 1 hour ago, bernhard said: I don't think that this is true any more with HTTP2 because browsers can download multiple files at once. That's why I didn't implement something like this in RockFrontend. But I'm not sure ? Me neither ? I had to write the script because https://pagespeed.web.dev/ complained about my multiple files. 1 Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now