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;
|
||
|
}
|
||
|
}
|
||
|
}
|