269 lines
8.6 KiB
PHP
269 lines
8.6 KiB
PHP
|
<?php
|
||
|
namespace WpAssetCleanUp\OptimiseAssets;
|
||
|
|
||
|
use WpAssetCleanUp\Main;
|
||
|
use WpAssetCleanUp\Menu;
|
||
|
use WpAssetCleanUp\MetaBoxes;
|
||
|
use WpAssetCleanUp\Misc;
|
||
|
|
||
|
/**
|
||
|
* Class MinifyJs
|
||
|
* @package WpAssetCleanUp\OptimiseAssets
|
||
|
*/
|
||
|
class MinifyJs
|
||
|
{
|
||
|
/**
|
||
|
* @param $jsContent
|
||
|
*
|
||
|
* @return string|string[]|null
|
||
|
*/
|
||
|
public static function applyMinification($jsContent)
|
||
|
{
|
||
|
if (class_exists('\MatthiasMullie\Minify\JS')) {
|
||
|
$sha1OriginalContent = sha1($jsContent);
|
||
|
$checkForAlreadyMinifiedShaOne = mb_strlen($jsContent) > 40000;
|
||
|
|
||
|
// Let's check if the content is already minified
|
||
|
// Save resources as the minify process can take time if the content is very large
|
||
|
// Limit the total number of entries to 100: if it's more than that, it's likely because there's dynamic JS altering on every page load
|
||
|
if ($checkForAlreadyMinifiedShaOne && OptimizeCommon::originalContentIsAlreadyMarkedAsMinified($sha1OriginalContent, 'scripts')) {
|
||
|
return $jsContent;
|
||
|
}
|
||
|
|
||
|
// Minify it
|
||
|
$alreadyMinified = false; // default
|
||
|
|
||
|
$minifier = new \MatthiasMullie\Minify\JS($jsContent);
|
||
|
$minifiedContent = trim($minifier->minify());
|
||
|
|
||
|
if (trim($minifiedContent) === trim(trim($jsContent, ';'))) {
|
||
|
$minifiedContent = $jsContent; // consider them the same if only the ';' at the end was stripped (it doesn't worth the resources that would be used)
|
||
|
$alreadyMinified = true;
|
||
|
}
|
||
|
|
||
|
// If the resulting content is the same, mark it as minified to avoid the minify process next time
|
||
|
if ($checkForAlreadyMinifiedShaOne && $alreadyMinified) {
|
||
|
// If the resulting content is the same, mark it as minified to avoid the minify process next time
|
||
|
OptimizeCommon::originalContentMarkAsAlreadyMinified( $sha1OriginalContent, 'scripts' );
|
||
|
}
|
||
|
|
||
|
return $minifiedContent;
|
||
|
}
|
||
|
|
||
|
return $jsContent;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $src
|
||
|
* @param string $handle
|
||
|
*
|
||
|
* @return bool
|
||
|
*/
|
||
|
public static function skipMinify($src, $handle = '')
|
||
|
{
|
||
|
// Things like WP Fastest Cache Toolbar JS shouldn't be minified and take up space on the server
|
||
|
if ($handle !== '' && in_array($handle, Main::instance()->skipAssets['scripts'])) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
$regExps = array(
|
||
|
'#/wp-content/plugins/wp-asset-clean-up(.*?).js#',
|
||
|
|
||
|
// Other libraries from the core that end in .min.js
|
||
|
'#/wp-includes/(.*?).min.js#',
|
||
|
|
||
|
// jQuery & jQuery Migrate
|
||
|
'#/wp-includes/js/jquery/jquery.js#',
|
||
|
'#/wp-includes/js/jquery/jquery-migrate.js#',
|
||
|
|
||
|
// Files within /wp-content/uploads/
|
||
|
// Files within /wp-content/uploads/ or /wp-content/cache/
|
||
|
// Could belong to plugins such as "Elementor, "Oxygen" etc.
|
||
|
//'#/wp-content/uploads/(.*?).js#',
|
||
|
'#/wp-content/cache/(.*?).js#',
|
||
|
|
||
|
// Already minified, and it also has a random name making the cache folder make bigger
|
||
|
'#/wp-content/bs-booster-cache/#',
|
||
|
|
||
|
// Elementor .min.js
|
||
|
'#/wp-content/plugins/elementor/assets/(.*?).min.js#',
|
||
|
|
||
|
// WooCommerce Assets
|
||
|
'#/wp-content/plugins/woocommerce/assets/js/(.*?).min.js#',
|
||
|
|
||
|
// Google Site Kit
|
||
|
// The files are already optimized (they just have comments once in a while)
|
||
|
// Minifying them causes some errors, so better to leave them load as they are
|
||
|
'#/wp-content/plugins/google-site-kit/#',
|
||
|
|
||
|
// TranslatePress Multilingual
|
||
|
'#/translatepress-multilingual/assets/js/trp-editor.js#',
|
||
|
|
||
|
);
|
||
|
|
||
|
$regExps = Misc::replaceRelPluginPath($regExps);
|
||
|
|
||
|
if (Main::instance()->settings['minify_loaded_js_exceptions'] !== '') {
|
||
|
$loadedJsExceptionsPatterns = trim(Main::instance()->settings['minify_loaded_js_exceptions']);
|
||
|
|
||
|
if (strpos($loadedJsExceptionsPatterns, "\n")) {
|
||
|
// Multiple values (one per line)
|
||
|
foreach (explode("\n", $loadedJsExceptionsPatterns) as $loadedJsExceptionPattern) {
|
||
|
$regExps[] = '#'.trim($loadedJsExceptionPattern).'#';
|
||
|
}
|
||
|
} else {
|
||
|
// Only one value?
|
||
|
$regExps[] = '#'.trim($loadedJsExceptionsPatterns).'#';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach ($regExps as $regExp) {
|
||
|
if ( preg_match( $regExp, $src ) || ( strpos($src, $regExp) !== false ) ) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $htmlSource
|
||
|
*
|
||
|
* @return mixed|string
|
||
|
*/
|
||
|
public static function minifyInlineScriptTags($htmlSource)
|
||
|
{
|
||
|
if (stripos($htmlSource, '<script') === false) {
|
||
|
return $htmlSource; // no SCRIPT tags, hmm
|
||
|
}
|
||
|
|
||
|
$skipTagsContaining = array_map( static function ( $toMatch ) {
|
||
|
return preg_quote($toMatch, '/');
|
||
|
}, array(
|
||
|
'data-wpacu-skip',
|
||
|
'/* <![CDATA[ */', // added via wp_localize_script()
|
||
|
'wpacu-google-fonts-async-load',
|
||
|
'wpacu-preload-async-css-fallback',
|
||
|
/* [wpacu_pro] */'data-wpacu-inline-js-file',/* [/wpacu_pro] */
|
||
|
'document.body.prepend(wpacuLinkTag',
|
||
|
'var wc_product_block_data = JSON.parse( decodeURIComponent(',
|
||
|
'/(^|\s)(no-)?customize-support(?=\s|$)/', // WP Core
|
||
|
'b[c] += ( window.postMessage && request ? \' \' : \' no-\' ) + cs;', // WP Core
|
||
|
'data-wpacu-own-inline-script', // Only shown to the admin, irrelevant for any optimization (save resources)
|
||
|
// [wpacu_pro]
|
||
|
'data-wpacu-inline-js-file', // already minified/optimized since the INLINE was generated from the cached file
|
||
|
// [/wpacu_pro]
|
||
|
));
|
||
|
|
||
|
// Do not perform another \DOMDocument call if it was done already somewhere else (e.g. CombineJs)
|
||
|
$fetchType = 'regex'; // 'regex' or 'dom'
|
||
|
|
||
|
if ($fetchType === 'regex') {
|
||
|
preg_match_all( '@(<script[^>]*?>).*?</script>@si', $htmlSource, $matchesScriptTags, PREG_SET_ORDER );
|
||
|
|
||
|
if ( $matchesScriptTags === null ) {
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
foreach ($matchesScriptTags as $matchedScript) {
|
||
|
if (isset($matchedScript[0]) && $matchedScript[0]) {
|
||
|
$originalTag = $matchedScript[0];
|
||
|
|
||
|
if (strpos($originalTag, 'src=') && strtolower(substr($originalTag, -strlen('></script>'))) === strtolower('></script>')) {
|
||
|
// Only inline SCRIPT tags allowed
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// No need to use extra resources as the tag is already minified
|
||
|
if ( preg_match( '/(' . implode( '|', $skipTagsContaining ) . ')/', $originalTag ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Only 'text/javascript' type is allowed for minification
|
||
|
$scriptType = Misc::getValueFromTag($originalTag, 'type') ?: 'text/javascript'; // default
|
||
|
|
||
|
if ($scriptType !== 'text/javascript') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$tagOpen = $matchedScript[1];
|
||
|
$withTagOpenStripped = substr($originalTag, strlen($tagOpen));
|
||
|
$originalTagContents = substr($withTagOpenStripped, 0, -strlen('</script>'));
|
||
|
|
||
|
$newTagContents = OptimizeJs::maybeAlterContentForInlineScriptTag( $originalTagContents, true );
|
||
|
|
||
|
if ( $newTagContents !== $originalTagContents ) {
|
||
|
$htmlSource = str_ireplace( '>' . $originalTagContents . '</script', '>' . $newTagContents . '</script', $htmlSource );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $htmlSource;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool
|
||
|
*/
|
||
|
public static function isMinifyJsEnabled()
|
||
|
{
|
||
|
if (defined('WPACU_IS_MINIFY_JS_ENABLED')) {
|
||
|
return WPACU_IS_MINIFY_JS_ENABLED;
|
||
|
}
|
||
|
|
||
|
// Request Minify On The Fly
|
||
|
// It will preview the page with JS minified
|
||
|
// Only if the admin is logged-in as it uses more resources (CPU / Memory)
|
||
|
if ( isset($_GET['wpacu_js_minify']) && Menu::userCanManageAssets()) {
|
||
|
self::isMinifyJsEnabledChecked('true');
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if ( isset($_REQUEST['wpacu_no_js_minify']) || // not on query string request (debugging purposes)
|
||
|
is_admin() || // not for Dashboard view
|
||
|
(! Main::instance()->settings['minify_loaded_js']) || // Minify JS has to be Enabled
|
||
|
(Main::instance()->settings['test_mode'] && ! Menu::userCanManageAssets()) ) { // Does not trigger if "Test Mode" is Enabled
|
||
|
self::isMinifyJsEnabledChecked('false');
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
$isSingularPage = defined('WPACU_CURRENT_PAGE_ID') && WPACU_CURRENT_PAGE_ID > 0 && is_singular();
|
||
|
|
||
|
if ($isSingularPage || Misc::isHomePage()) {
|
||
|
// If "Do not minify JS on this page" is checked in "Asset CleanUp: Options" side meta box
|
||
|
if ($isSingularPage) {
|
||
|
$pageOptions = MetaBoxes::getPageOptions( WPACU_CURRENT_PAGE_ID ); // Singular page
|
||
|
} else {
|
||
|
$pageOptions = MetaBoxes::getPageOptions(0, 'front_page'); // Home page
|
||
|
}
|
||
|
|
||
|
if ( isset( $pageOptions['no_js_minify'] ) && $pageOptions['no_js_minify'] ) {
|
||
|
self::isMinifyJsEnabledChecked('false');
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (OptimizeJs::isOptimizeJsEnabledByOtherParty('if_enabled')) {
|
||
|
self::isMinifyJsEnabledChecked('false');
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
self::isMinifyJsEnabledChecked('true');
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $value
|
||
|
*/
|
||
|
public static function isMinifyJsEnabledChecked($value)
|
||
|
{
|
||
|
if (! defined('WPACU_IS_MINIFY_JS_ENABLED')) {
|
||
|
if ($value === 'true') {
|
||
|
define( 'WPACU_IS_MINIFY_JS_ENABLED', true );
|
||
|
} elseif ($value === 'false') {
|
||
|
define( 'WPACU_IS_MINIFY_JS_ENABLED', false );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|