201 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			201 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace Sphere\Debloat;
 | |
| 
 | |
| use Sphere\Debloat\Base\Asset;
 | |
| use Sphere\Debloat\OptimizeCss\Stylesheet;
 | |
| use MatthiasMullie\Minify;
 | |
| 
 | |
| /**
 | |
|  * Minifer for assets with cache integration.
 | |
|  * 
 | |
|  * @author  asadkn
 | |
|  * @since   1.0.0
 | |
|  */
 | |
| class Minifier
 | |
| {
 | |
| 	protected $asset_type = 'js';
 | |
| 
 | |
| 	/**
 | |
| 	 * @var Sphere\Debloat\OptimizeCss\Stylesheet|Sphere\Debloat\OptimizeJs\Script
 | |
| 	 */
 | |
| 	protected $asset;
 | |
| 
 | |
| 	/**
 | |
| 	 * @param \Sphere\Debloat\Base\Asset $asset
 | |
| 	 */
 | |
| 	public function __construct(Asset $asset)
 | |
| 	{
 | |
| 		if ($asset instanceof Stylesheet) {
 | |
| 			$this->asset_type = 'css';
 | |
| 		}
 | |
| 
 | |
| 		$this->asset = $asset;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Minify the asset, cache it, and replace its URL in the asset object.
 | |
| 	 * 
 | |
| 	 * @uses Plugin::file_cache()
 | |
| 	 * @uses Plugin::file_system()
 | |
| 	 * 
 | |
| 	 * @return string URL of the minified file.
 | |
| 	 */
 | |
| 	public function process()
 | |
| 	{
 | |
| 		// Not for inline assets.
 | |
| 		if (!$this->asset->url) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		// Debugging scripts. Do not minify.
 | |
| 		if (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$file = Plugin::file_cache()->get_url($this->asset->url);
 | |
| 		if (!$file) {
 | |
| 			$minify = $this->minify();
 | |
| 			if (!$minify) {
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			// For CSS assets, convert URLs to fully qualified.
 | |
| 			$this->maybe_convert_urls();
 | |
| 			
 | |
| 			if (!Plugin::file_cache()->set($this->asset->url, $this->asset->content)) {
 | |
| 				return;
 | |
| 			}
 | |
| 			
 | |
| 			$file = Plugin::file_cache()->get_url($this->asset->url);
 | |
| 		}
 | |
| 
 | |
| 		$this->asset->minified_url = $file;
 | |
| 		return $file;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Minify the file using the URL in the asset object.
 | |
| 	 *
 | |
| 	 * @return string|boolean
 | |
| 	 */
 | |
| 	public function minify()
 | |
| 	{
 | |
| 		// We support google fonts remote fetch.
 | |
| 		if (
 | |
| 			Plugin::options()->optimize_gfonts_inline && 
 | |
| 			$this->asset instanceof Stylesheet && 
 | |
| 			$this->asset->is_google_fonts()
 | |
| 		) {
 | |
| 			$this->fetch_remote_content();
 | |
| 		
 | |
| 		} else {
 | |
| 			// We minify and cache local source only for now.
 | |
| 			$source_file = Plugin::file_system()->url_to_local($this->asset->url);
 | |
| 			if (!$source_file) {
 | |
| 				return false;
 | |
| 			}
 | |
| 
 | |
| 			$source = Plugin::file_system()->get_contents($source_file);
 | |
| 			$this->asset->content = $source;
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * We want a cached file with source data whether existing is minified or not - as 
 | |
| 		 * post-processing is needed for URLs when inlining the CSS. 
 | |
| 		 * 
 | |
| 		 * For JS, caching the already min files also serves the purpose of not testing them
 | |
| 		 * again, unnecessarily.
 | |
| 		 */
 | |
| 		if ($this->is_content_minified()) {
 | |
| 			return $this->asset->content;
 | |
| 		}
 | |
| 
 | |
| 		Util\debug_log('Minifying: ' . $this->asset->url);
 | |
| 
 | |
| 		// JS minifier.
 | |
| 		if ($this->asset_type === 'js') {
 | |
| 
 | |
| 			// Improper handling for older webpack: https://github.com/matthiasmullie/minify/issues/375
 | |
| 			if (strpos($source, 'var __webpack_require__ = function (moduleId)') !== false) {
 | |
| 				return false;
 | |
| 			}
 | |
| 
 | |
| 			$minifier = new Minify\JS($this->asset->content);
 | |
| 			$this->asset->content = $minifier->minify();
 | |
| 
 | |
| 		} else {
 | |
| 
 | |
| 			// CSS minifier. Set content and convert urls.
 | |
| 			$minifier = new Minify\CSS($this->asset->content);
 | |
| 			$this->asset->content = $minifier->minify();
 | |
| 		}
 | |
| 		
 | |
| 		return $this->asset->content;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Convert URLs for CSS assets.
 | |
| 	 *
 | |
| 	 * @return void
 | |
| 	 */
 | |
| 	public function maybe_convert_urls()
 | |
| 	{
 | |
| 		if ($this->asset_type !== 'css') {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		// Check if any non-data URLs exist.
 | |
| 		if (!preg_match('/url\((?![\'"\s]*data:)/', $this->asset->content)) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$this->asset->convert_urls();
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get remote asset content and add to asset content.
 | |
| 	 *
 | |
| 	 * @return void
 | |
| 	 */
 | |
| 	public function fetch_remote_content()
 | |
| 	{
 | |
| 		$request = wp_remote_get($this->asset->url, [
 | |
| 			'timeout' => 5,
 | |
| 			// For google fonts mainly.
 | |
| 			'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36',
 | |
| 		]);
 | |
| 
 | |
| 		if (is_wp_error($request) || empty($request['body'])) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$this->asset->content = $request['body'];
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Check if provided content is already minified.
 | |
| 	 *
 | |
| 	 * @return boolean
 | |
| 	 */
 | |
| 	public function is_content_minified($content = '')
 | |
| 	{
 | |
| 		$content = $content ?: $this->asset->content;
 | |
| 
 | |
| 		// Already minified asset.
 | |
| 		if (preg_match('/[\-\.]min\.(js|css)/', $this->asset->url)) {
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		$content = trim($content);
 | |
| 		if (!$content) {
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		// Hacky, but will do. 
 | |
| 		$new_lines = substr_count($content, "\n", 0, min(strlen($content), 2000));
 | |
| 		if ($new_lines < 5) {
 | |
| 			return true;
 | |
| 		}
 | |
| 	}
 | |
| } |