wp_back/wp-content/plugins/wp-asset-clean-up/classes/HardcodedAssets.php

884 lines
32 KiB
PHP
Raw Permalink Normal View History

2024-05-20 15:37:46 +03:00
<?php
namespace WpAssetCleanUp;
use WpAssetCleanUp\OptimiseAssets\OptimizeCommon;
use WpAssetCleanUp\OptimiseAssets\OptimizeJs;
/**
* Class HardcodedAssets
* @package WpAssetCleanUp
*/
class HardcodedAssets
{
/**
*
*/
public static function init()
{
add_action( 'init', static function() {
if (Main::instance()->isGetAssetsCall) {
// Case 1: An AJAX call is made from the Dashboard
self::initBufferingForAjaxCallFromTheDashboard();
} elseif (self::useBufferingForEditFrontEndView()) {
// Case 2: The logged-in admin manages the assets from the front-end view
self::initBufferingForFrontendManagement();
}
});
}
/**
*
*/
public static function initBufferingForAjaxCallFromTheDashboard()
{
ob_start();
add_action('shutdown', static function() {
$htmlSource = '';
// We'll need to get the number of ob levels we're in, so that we can iterate over each, collecting
// that buffer's output into the final output.
$htmlSourceLevel = ob_get_level();
for ($wpacuI = 0; $wpacuI < $htmlSourceLevel; $wpacuI++) {
$htmlSource .= ob_get_clean();
}
$anyHardCodedAssets = HardcodedAssets::getAll($htmlSource); // Fetch all for this type of request
echo str_replace('{wpacu_hardcoded_assets}', $anyHardCodedAssets, $htmlSource);
}, 0);
}
/**
*
*/
public static function initBufferingForFrontendManagement()
{
// Used to print the hardcoded CSS/JS
ob_start();
add_action('shutdown', static function() {
if (! defined('NEXTEND_SMARTSLIDER_3_URL_PATH')) {
ob_flush();
}
$htmlSource = '';
// We'll need to get the number of ob levels we're in, so that we can iterate over each, collecting
// that buffer's output into the final output.
$htmlSourceLevel = ob_get_level();
for ($wpacuI = 0; $wpacuI < $htmlSourceLevel; $wpacuI++) {
$htmlSource .= ob_get_clean();
}
echo OptimizeCommon::alterHtmlSource($htmlSource);
}, 0);
}
/**
* @return bool
*/
public static function useBufferingForEditFrontEndView()
{
// The logged-in admin needs to be outside the Dashboard (in the front-end view)
// "Manage in the Front-end" is enabled in "Settings" -> "Plugin Usage Preferences"
return (Main::instance()->frontendShow() && ! is_admin() && Menu::userCanManageAssets() && ! Main::instance()->isGetAssetsCall);
}
/**
* @param $htmlSource
* @param bool $encodeIt - if set to "false", it's mostly for testing purposes
*
* @return string|array
*/
public static function getAll($htmlSource, $encodeIt = true)
{
$htmlSourceAlt = CleanUp::removeHtmlComments($htmlSource, true);
$collectLinkStyles = true; // default
$collectScripts = true; // default
$hardCodedAssets = array(
'link_and_style_tags' => array(), // LINK (rel="stylesheet") & STYLE (inline)
'script_nosrc_and_inline_tags' => array(), // SCRIPT (with "src" attribute) & SCRIPT (inline)
);
$matchesSourcesFromTags = array();
$stickToRegEx = true;
if ( $collectLinkStyles ) {
if ( ! $stickToRegEx && Misc::isDOMDocumentOn() ) {
$domDoc = Misc::initDOMDocument();
$domDoc->loadHTML($htmlSourceAlt);
$selector = new \DOMXPath($domDoc);
$domTagQuery = $selector->query('//link[@rel="stylesheet"]|//style|//script|//noscript');
if (count($domTagQuery) > 1) {
foreach($domTagQuery as $tagFound) {
$tagType = in_array($tagFound->nodeName, array('link', 'style')) ? 'css' : 'js';
if (self::skipTagIfNotRelevant( Misc::getOuterHTML( $tagFound ), 'whole_tag', $tagType)) {
continue; // no point in wasting more resources as the tag will never be shown, since it's irrelevant
}
if ( $tagFound->hasAttributes() ) {
foreach ( $tagFound->attributes as $attr ) {
if ( self::skipTagIfNotRelevant( $attr->nodeName, 'attribute', $tagType ) ) {
continue 2;
}
}
}
if ($tagFound->nodeName === 'link') {
if ( ! $tagFound->hasAttributes() ) {
continue;
}
$linkTagParts = array();
$linkTagParts[] = '<link ';
foreach ($tagFound->attributes as $attr) {
$attrName = $attr->nodeName;
$attrValue = $attr->nodeValue;
if ($attrName) {
if ($attrValue !== '') {
$linkTagParts[] = '(\s+|)' . preg_quote($attrName, '/') . '(\s+|)=(\s+|)(|"|\')' . preg_quote($attrValue, '/') . '(|"|\')(|\s+)';
} else {
$linkTagParts[] = '(\s+|)' . preg_quote($attrName, '/') . '(|((\s+|)=(\s+|)(|"|\')(|"|\')))';
}
}
}
$linkTagParts[] = '(|\s+)(|/)>';
$linkTagFinalRegExPart = implode('', $linkTagParts);
preg_match_all(
'#'.$linkTagFinalRegExPart.'#Umi',
$htmlSource,
$matchSourceFromTag,
PREG_SET_ORDER
);
// It always has to be a match from the DOM generated tag
// Otherwise, default it to RegEx
if ( empty($matchSourceFromTag) || ! (isset($matchSourceFromTag[0][0]) && ! empty($matchSourceFromTag[0][0])) ) {
$stickToRegEx = true;
break;
}
$matchesSourcesFromTags[] = array('link_tag' => $matchSourceFromTag[0][0]);
}
}
if (! $stickToRegEx) {
$shaOneToOriginal = array();
$htmlSourceAltEncoded = $htmlSourceAlt;
foreach($domTagQuery as $tagFound) {
if ( $tagFound->nodeValue && in_array( $tagFound->nodeName, array( 'style', 'script', 'noscript' ) ) ) {
if (strpos($htmlSourceAlt, $tagFound->nodeValue) === false) {
$stickToRegEx = true;
break;
}
$shaOneToOriginal[sha1($tagFound->nodeValue)] = $tagFound->nodeValue;
$htmlSourceAltEncoded = str_replace(
$tagFound->nodeValue,
'/*[wpacu]*/' . sha1($tagFound->nodeValue) . '/*[/wpacu]*/',
$htmlSourceAltEncoded
);
}
}
$domDocForTwo = Misc::initDOMDocument();
$domDocForTwo->loadHTML($htmlSourceAltEncoded);
$selectorTwo = new \DOMXPath($domDocForTwo);
$domTagQueryTwo = $selectorTwo->query('//style|//script|//noscript');
foreach($domTagQueryTwo as $tagFoundTwo) {
$tagType = in_array($tagFoundTwo->nodeName, array('link', 'style')) ? 'css' : 'js';
if ( $tagFoundTwo->hasAttributes() ) {
foreach ( $tagFoundTwo->attributes as $attr ) {
if ( self::skipTagIfNotRelevant( $attr->nodeName, 'attribute', $tagType ) ) {
continue 2;
}
}
}
$tagParts = array();
$tagParts[] = '<'.$tagFoundTwo->nodeName;
foreach ($tagFoundTwo->attributes as $attr) {
$attrName = $attr->nodeName;
$attrValue = $attr->nodeValue;
if ($attrName) {
if ($attrValue !== '') {
$tagParts[] = '(\s+|)' . preg_quote($attrName, '/') . '(\s+|)=(\s+|)(|"|\')' . preg_quote($attrValue, '/') . '(|"|\')(|\s+)';
} else {
$tagParts[] = '(\s+|)' . preg_quote($attrName, '/') . '(|((\s+|)=(\s+|)(|"|\')(|"|\')))';
}
}
}
$tagParts[] = '(|\s+)>';
if ($tagFoundTwo->nodeValue) {
$tagParts[] = preg_quote($tagFoundTwo->nodeValue, '/');
}
$tagParts[] = '</'.$tagFoundTwo->nodeName.'>';
$tagFinalRegExPart = implode('', $tagParts);
preg_match_all(
'#'.$tagFinalRegExPart.'#Umi',
$htmlSourceAltEncoded,
$matchSourceFromTagTwo,
PREG_SET_ORDER
);
// It always has to be a match from the DOM generated tag
// Otherwise, default it to RegEx
if ( empty($matchSourceFromTagTwo) || ! (isset($matchSourceFromTagTwo[0][0]) && ! empty($matchSourceFromTagTwo[0][0])) ) {
$stickToRegEx = true;
break;
}
$encodedNodeValue = Misc::extractBetween($matchSourceFromTagTwo[0][0], '/*[wpacu]*/', '/*[/wpacu]*/');
$matchedTag = str_replace('/*[wpacu]*/'.$encodedNodeValue.'/*[/wpacu]*/', $shaOneToOriginal[$encodedNodeValue], $matchSourceFromTagTwo[0][0]);
$tagTypeForReference = ($tagFoundTwo->nodeName === 'style') ? 'style_tag' : 'script_noscript_tag';
$matchesSourcesFromTags[] = array($tagTypeForReference => $matchedTag);
}
}
}
}
/*
* [START] Collect Hardcoded LINK (stylesheet) & STYLE tags
*/
if ($stickToRegEx || ! Misc::isDOMDocumentOn()) {
preg_match_all(
'#(?=(?P<link_tag><link[^>]*stylesheet[^>]*(>)))|(?=(?P<style_tag><style[^>]*?>.*</style>))#Umsi',
$htmlSourceAlt,
$matchesSourcesFromTags,
PREG_SET_ORDER
);
}
if ( ! empty( $matchesSourcesFromTags ) ) {
// Only the hashes are set
// For instance, 'd1eae32c4e99d24573042dfbb71f5258a86e2a8e' is the hash for the following script:
/*
* <style media="print">#wpadminbar { display:none; }</style>
*/
$stripsSpecificStylesHashes = array(
'5ead5f033961f3b8db362d2ede500051f659dd6d',
'25bd090513716c34b48b0495c834d2070088ad24'
);
// Sometimes, the hash checking might fail (if there's a small change to the JS content)
// Consider using a fallback verification by checking the actual content
$stripsSpecificStylesContaining = array(
'<style media="print">#wpadminbar { display:none; }</style>',
'id="edd-store-menu-styling"',
'#wp-admin-bar-gform-forms'
);
foreach ( $matchesSourcesFromTags as $matchedTag ) {
// LINK "stylesheet" tags (if any)
if ( isset( $matchedTag['link_tag'] ) && trim( $matchedTag['link_tag'] ) !== '' && ( trim( strip_tags( $matchedTag['link_tag'] ) ) === '' ) ) {
$matchedTagOutput = trim( $matchedTag['link_tag'] );
// Own plugin assets and enqueued ones since they aren't hardcoded
if (self::skipTagIfNotRelevant($matchedTagOutput)) {
continue;
}
$hardCodedAssets['link_and_style_tags'][] = $matchedTagOutput;
}
// STYLE inline tags (if any)
if ( isset( $matchedTag['style_tag'] ) && trim( $matchedTag['style_tag'] ) !== '' ) {
$matchedTagOutput = trim( $matchedTag['style_tag'] );
/*
* Strip certain STYLE tags irrelevant for the list (e.g. related to the WordPress Admin Bar, etc.)
*/
if ( in_array( self::determineHardcodedAssetSha1( $matchedTagOutput ), $stripsSpecificStylesHashes ) ) {
continue;
}
foreach ( $stripsSpecificStylesContaining as $cssContentTargeted ) {
if ( strpos( $matchedTagOutput, $cssContentTargeted ) !== false ) {
continue 2; // applies for this "foreach": ($matchesSourcesFromTags as $matchedTag)
}
}
// Own plugin assets and enqueued ones since they aren't hardcoded
if (self::skipTagIfNotRelevant($matchedTagOutput)) {
continue;
}
foreach ( wp_styles()->done as $cssHandle ) {
if ( strpos( $matchedTagOutput,
'<style id=\'' . trim( $cssHandle ) . '-inline-css\'' ) !== false ) {
// Do not consider the STYLE added via WordPress with wp_add_inline_style() as it's not hardcoded
continue 2;
}
}
$hardCodedAssets['link_and_style_tags'][] = $matchedTagOutput;
}
}
}
/*
* [END] Collect Hardcoded LINK (stylesheet) & STYLE tags
*/
}
if ($collectScripts) {
/*
* [START] Collect Hardcoded SCRIPT (src/inline)
*/
if ($stickToRegEx || ! Misc::isDOMDocumentOn()) {
preg_match_all( '@<script[^>]*?>.*?</script>|<noscript[^>]*?>.*?</noscript>@si', $htmlSourceAlt, $matchesScriptTags, PREG_SET_ORDER );
} else {
$matchesScriptTags = array();
if (! empty($matchesSourcesFromTags)) {
foreach ($matchesSourcesFromTags as $matchedTag) {
if (isset($matchedTag['script_noscript_tag']) && $matchedTag['script_noscript_tag']) {
$matchesScriptTags[][0] = $matchedTag['script_noscript_tag'];
}
}
}
}
$allInlineAssocWithJsHandle = array();
if ( isset( wp_scripts()->done ) && ! empty( wp_scripts()->done ) ) {
foreach ( wp_scripts()->done as $assetHandle ) {
// Now, go through the list of inline SCRIPTs associated with an enqueued SCRIPT (with "src" attribute)
// And make sure they do not show to the hardcoded list, since they are related to the handle, and they are stripped when the handle is dequeued
$anyInlineAssocWithJsHandle = OptimizeJs::getInlineAssociatedWithScriptHandle( $assetHandle, wp_scripts()->registered, 'handle' );
if ( ! empty( $anyInlineAssocWithJsHandle ) ) {
foreach ( $anyInlineAssocWithJsHandle as $jsInlineTagContent ) {
if ( trim( $jsInlineTagContent ) === '' ) {
continue;
}
$allInlineAssocWithJsHandle[] = trim($jsInlineTagContent);
}
}
}
$allInlineAssocWithJsHandle = array_unique($allInlineAssocWithJsHandle);
}
// Go through the hardcoded SCRIPT tags
if ( isset( $matchesScriptTags ) && ! empty( $matchesScriptTags ) ) {
// Only the hashes are set
// For instance, 'd1eae32c4e99d24573042dfbb71f5258a86e2a8e' is the hash for the following script:
/*
* <script>
(function() {
var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
request = true;
b[c] = b[c].replace( rcs, ' ' );
// The customizer requires postMessage and CORS (if the site is cross domain)
b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
}());
</script>
*/
$stripsSpecificScriptsHashes = array(
'd1eae32c4e99d24573042dfbb71f5258a86e2a8e',
'1a8f46f9f33e5d95919620df54781acbfa9efff7'
);
// Sometimes, the hash checking might fail (if there's a small change to the JS content)
// Consider using a fallback verification by checking the actual content
$stripsSpecificScriptsContaining = array(
'// The customizer requires postMessage and CORS (if the site is cross domain)',
'b[c] += ( window.postMessage && request ? \' \' : \' no-\' ) + cs;',
"(function(){var request,b=document.body,c='className',cs='customize-support',rcs=new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');request=!0;b[c]=b[c].replace(rcs,' ');b[c]+=(window.postMessage&&request?' ':' no-')+cs}())",
'document.body.className = document.body.className.replace( /(^|\s)(no-)?customize-support(?=\s|$)/, \'\' ) + \' no-customize-support\'',
"c = c.replace(/woocommerce-no-js/, 'woocommerce-js');" // WooCommerce related
);
foreach ( $matchesScriptTags as $matchedTag ) {
if ( isset( $matchedTag[0] ) && $matchedTag[0]
&& (stripos( $matchedTag[0], '<script' ) === 0 || stripos( $matchedTag[0], '<noscript' ) === 0) ) {
$matchedTagOutput = trim( $matchedTag[0] );
// Own plugin assets and enqueued ones since they aren't hardcoded
if (self::skipTagIfNotRelevant($matchedTagOutput, 'whole_tag', 'js', array('all_inline_assoc_with_js_handle' => $allInlineAssocWithJsHandle))) {
continue;
}
/*
* Strip certain SCRIPT tags irrelevant for the list (e.g. related to WordPress Customiser, Admin Bar, etc.)
*/
if ( in_array( self::determineHardcodedAssetSha1( $matchedTagOutput ), $stripsSpecificScriptsHashes ) ) {
continue;
}
foreach ( $stripsSpecificScriptsContaining as $jsContentTargeted ) {
if ( strpos( $matchedTagOutput, $jsContentTargeted ) !== false ) {
continue 2; // applies for this "foreach": ($matchesScriptTags as $matchedTag)
}
}
$hardCodedAssets['script_src_or_inline_and_noscript_inline_tags'][] = trim( $matchedTag[0] );
}
}
}
/*
* [END] Collect Hardcoded SCRIPT (src/inline)
*/
}
if ($stickToRegEx && ! empty($hardCodedAssets['link_and_style_tags']) && ! empty($hardCodedAssets['script_src_or_inline_and_noscript_inline_tags'])) {
$hardCodedAssets = self::removeAnyLinkTagsThatMightBeDetectedWithinScriptTags( $hardCodedAssets );
}
$tagsWithinConditionalComments = self::extractHtmlFromConditionalComments( $htmlSourceAlt );
if (Main::instance()->isGetAssetsCall) {
// AJAX call within the Dashboard
$hardCodedAssets['within_conditional_comments'] = $tagsWithinConditionalComments;
}
if ($encodeIt) {
return base64_encode( wp_json_encode( $hardCodedAssets ) );
}
return $hardCodedAssets;
}
/**
* @param $value
* @param $via ('whole_tag', 'attribute')
* @param string $type ('css', 'js')
* @param array $extras ('all_inline_assoc_with_js_handle')
*
* @return bool
*/
public static function skipTagIfNotRelevant($value, $via = 'whole_tag', $type = 'css', $extras = array())
{
if ($via === 'whole_tag') {
if ($type === 'css') {
if ( strpos( $value, 'data-wpacu-style-handle=' ) !== false ) {
// skip the SCRIPT with src that was enqueued properly and keep the hardcoded ones
return true;
}
if ( ( strpos( $value, 'data-wpacu-own-inline-style=' ) !== false ) ||
( strpos( $value, 'data-wpacu-inline-css-file=' ) !== false ) ) {
// remove plugin's own STYLE tags as they are not relevant in this context
return true;
}
// Do not add to the list elements such as Emojis (not relevant for hard-coded tags)
if ( strpos( $value, 'img.wp-smiley' ) !== false
&& strpos( $value, 'img.emoji' ) !== false
&& strpos( $value, '!important;' ) !== false ) {
return true;
}
}
if ($type === 'js') {
if ( strpos( $value, 'data-wpacu-script-handle=' ) !== false ) {
// skip the SCRIPT with src that was enqueued properly and keep the hardcoded ones
return true;
}
if ( ( strpos( $value, 'data-wpacu-own-inline-script=' ) !== false ) ||
( strpos( $value, 'data-wpacu-inline-js-file=' ) !== false ) ) {
// skip plugin's own SCRIPT tags as they are not relevant in this context
return true;
}
if ( strpos( $value, 'wpacu-preload-async-css-fallback' ) !== false ) {
// skip plugin's own SCRIPT tags as they are not relevant in this context
return true;
}
if ( strpos( $value, 'window._wpemojiSettings' ) !== false
&& strpos( $value, 'twemoji' ) !== false ) {
return true;
}
// Check the type and only allow SCRIPT tags with type='text/javascript' or no type at all (it will default to 'text/javascript')
$matchedTagInner = strip_tags( $value );
$matchedTagOnlyTags = str_replace( $matchedTagInner, '', $value );
$scriptType = Misc::getValueFromTag($matchedTagOnlyTags, 'type') ?: 'text/javascript';
if ( strpos( $scriptType, 'text/javascript' ) === false ) {
return true;
}
$allInlineAssocWithJsHandle = isset($extras['all_inline_assoc_with_js_handle']) ? $extras['all_inline_assoc_with_js_handle'] : array();
$hasSrc = false;
if (strpos($matchedTagOnlyTags, ' src=') !== false) {
$hasSrc = true;
}
if ( ! $hasSrc && ! empty( $allInlineAssocWithJsHandle ) ) {
preg_match_all("'<script[^>]*?>(.*?)</script>'si", $value, $matchesFromTagOutput);
$matchedTagOutputInner = isset($matchesFromTagOutput[1][0]) && trim($matchesFromTagOutput[1][0])
? trim($matchesFromTagOutput[1][0]) : false;
$matchedTagOutputInnerCleaner = $matchedTagOutputInner;
$stripStrStart = '/* <![CDATA[ */';
$stripStrEnd = '/* ]]> */';
if (strpos($matchedTagOutputInnerCleaner, $stripStrStart) === 0
&& Misc::endsWith($matchedTagOutputInnerCleaner, '/* ]]> */')) {
$matchedTagOutputInnerCleaner = substr($matchedTagOutputInnerCleaner, strlen($stripStrStart));
$matchedTagOutputInnerCleaner = substr($matchedTagOutputInnerCleaner, 0, -strlen($stripStrEnd));
$matchedTagOutputInnerCleaner = trim($matchedTagOutputInnerCleaner);
}
if (in_array($matchedTagOutputInnerCleaner, $allInlineAssocWithJsHandle)) {
return true;
}
}
}
}
if ($via === 'attribute') {
if ( $type === 'css' ) {
$possibleSignatures = array(
'data-wpacu-style-handle',
'data-wpacu-own-inline-style',
'data-wpacu-inline-css-file'
);
} else {
$possibleSignatures = array(
'data-wpacu-script-handle',
'data-wpacu-own-inline-script',
'data-wpacu-inline-js-file',
'wpacu-preload-async-css-fallback'
);
}
if (in_array($value, $possibleSignatures)) {
return true;
}
}
return false; // default
}
/**
*
* @param $hardcodedAssets
*
* @return mixed
*/
public static function removeAnyLinkTagsThatMightBeDetectedWithinScriptTags($hardcodedAssets)
{
foreach ($hardcodedAssets['link_and_style_tags'] as $cssTagIndex => $cssTag) {
if ($cssTag) {
foreach ($hardcodedAssets['script_src_or_inline_and_noscript_inline_tags'] as $scriptTag) {
if (strpos($scriptTag, $cssTag) !== false) {
// e.g. could be '<script>var linkToCss="<link href='[path_to_custom_css_file_here]'>";</script>'
unset($hardcodedAssets['link_and_style_tags'][$cssTagIndex]);
}
}
}
}
return $hardcodedAssets;
}
/**
* @param $htmlSource
*
* @return array
*/
public static function extractHtmlFromConditionalComments($htmlSource)
{
preg_match_all('#<!--\[if(.*?)]>(<!-->|-->|\s|)(.*?)(<!--<!|<!)\[endif]-->#si', $htmlSource, $matchedContent);
if (isset($matchedContent[1], $matchedContent[3]) && ! empty($matchedContent[1]) && ! empty($matchedContent[3])) {
$conditions = array_map('trim', $matchedContent[1]);
$tags = array_map('trim', $matchedContent[3]);
return array(
'conditions' => $conditions,
'tags' => $tags,
);
}
return array();
}
/**
* @param $targetedTag
* @param $contentWithinConditionalComments
*
* @return bool
*/
public static function isWithinConditionalComment($targetedTag, $contentWithinConditionalComments)
{
if (empty($contentWithinConditionalComments)) {
return false;
}
$targetedTag = trim($targetedTag);
foreach ($contentWithinConditionalComments['tags'] as $tagIndex => $tagFromList) {
$tagFromList = trim($tagFromList);
if ($targetedTag === $tagFromList || strpos($targetedTag, $tagFromList) !== false) {
return $contentWithinConditionalComments['conditions'][$tagIndex]; // Stops here and returns the condition
}
}
return false; // Not within a conditional comment (most cases)
}
/**
* @param $htmlTag
*
* @return bool|string
*/
public static function belongsTo($htmlTag)
{
$belongList = array(
'wpcf7recaptcha.' => '"Contact Form 7" plugin',
'c = c.replace(/woocommerce-no-js/, \'woocommerce-js\');' => '"WooCommerce" plugin',
'.woocommerce-product-gallery{ opacity: 1 !important; }' => '"WooCommerce" plugin',
'-ss-slider-3' => '"Smart Slider 3" plugin',
'N2R(["nextend-frontend","smartslider-frontend","smartslider-simple-type-frontend"]' => '"Smart Slider 3" plugin',
'function setREVStartSize' => '"Slider Revolution" plugin',
'jQuery(\'.rev_slider_wrapper\')' => '"Slider Revolution" plugin',
'jQuery(\'#wp-admin-bar-revslider-default' => '"Slider Revolution" plugin'
);
foreach ($belongList as $ifContains => $isFromSource) {
if ( strpos( $htmlTag, $ifContains) !== false ) {
return $isFromSource;
}
}
return false;
}
/**
* @param $tagOutput
*
* @return string
*/
public static function determineHardcodedAssetSha1($tagOutput)
{
// Look if the "href" or "src" ends with '.css' or '.js'
// Only hash the actual path to the file
// In case the tag changes (e.g. an attribute will be added), the tag will be considered the same for the plugin rules
// To avoid the rules from not working / e.g. the file could have a dynamic "?ver=" at the end
if ( ! (stripos($tagOutput, '<link') !== false || stripos($tagOutput, '<style') !== false
|| stripos($tagOutput, '<script') !== false || stripos($tagOutput, '<noscript') !== false) ) {
return sha1( $tagOutput ); // default
}
$isLinkWithHref = (stripos($tagOutput, '<link') !== false) && preg_match('#href(\s+|)=(\s+|)(["\'])(.*)(["\'])#Usmi', $tagOutput);
$isScriptWithSrc = (stripos($tagOutput, '<script') !== false) && preg_match('#src(\s+|)=(\s+|)(["\'])(.*)(["\'])|src(\s+|)=(\s+|)(.*)(\s+)#Usmi', $tagOutput);
if ($isLinkWithHref || $isScriptWithSrc) {
return self::determineHardcodedAssetSha1ForAssetsWithSource($tagOutput);
}
if (stripos($tagOutput, '<style') !== false || stripos($tagOutput, '<script') !== false || stripos($tagOutput, '<noscript') !== false) {
return self::determineHardcodedAssetSha1ForAssetsWithoutSource($tagOutput);
}
return sha1( $tagOutput ); // default
}
/**
// In case there are STYLE tags and SCRIPT tags without any SRC, make sure to consider only the content of the tag as a reference
// e.g. if the user updates <style type="text/css"> to <style> the tag should be considered the same if the content is the same
// also, do not consider any whitespaces from the beginning and ending of the tag's content
*
* @param $tagOutput
*
* @return string
*/
public static function determineHardcodedAssetSha1ForAssetsWithoutSource($tagOutput)
{
if (stripos($tagOutput, '<style') !== false) {
preg_match_all('@(<style[^>]*?>)(.*?)</style>@si', $tagOutput, $matches);
if (isset($matches[0][0], $matches[2][0]) && strlen($tagOutput) === strlen($matches[0][0])) {
return sha1( trim($matches[2][0]) ); // the hashed content of the STYLE tag
}
} elseif (stripos($tagOutput, '<script') !== false) {
preg_match_all('@(<script[^>]*?>)(.*?)</script>@si', $tagOutput, $matches);
if (isset($matches[0][0], $matches[2][0]) && strlen($tagOutput) === strlen($matches[0][0])) {
return sha1( trim($matches[2][0]) ); // the hashed content of the SCRIPT tag
}
} elseif (stripos($tagOutput, '<noscript') !== false) {
preg_match_all('@(<noscript[^>]*?>)(.*?)</noscript>@si', $tagOutput, $matches);
if (isset($matches[0][0], $matches[2][0]) && strlen($tagOutput) === strlen($matches[0][0])) {
return sha1( trim($matches[2][0]) ); // the hashed content of the NOSCRIPT tag
}
}
return sha1($tagOutput);
}
/**
* Only the LINK tags and SCRIPT tags with the "href" and "src" attributes would be considered
*
* @param $tagOutput
*
* @return string
*/
public static function determineHardcodedAssetSha1ForAssetsWithSource($tagOutput)
{
if ($finalCleanSource = self::getRelSourceFromTagOutputForReference($tagOutput)) {
return sha1($finalCleanSource);
}
return sha1( $tagOutput ); // default
}
/**
* @param $tagOutput
*
* @return array|false|string|string[]
*/
public static function getRelSourceFromTagOutputForReference($tagOutput)
{
if (stripos($tagOutput, 'href') !== false && stripos($tagOutput, 'stylesheet') !== false && stripos(trim($tagOutput), '<link') === 0) {
$attrToGet = 'href';
$extToCheck = 'css';
} else {
$attrToGet = 'src';
$extToCheck = 'js';
}
$sourceValue = Misc::getValueFromTag($tagOutput, $attrToGet);
if (! $sourceValue) {
return false;
}
if ( stripos( $sourceValue, '.' . $extToCheck . '?' ) !== false ) {
list( $cleanSource ) = explode( '.' . $extToCheck . '?', $sourceValue );
$finalCleanSource = $cleanSource . '.' . $extToCheck;
} else {
$finalCleanSource = $sourceValue;
}
if ( $finalCleanSource ) {
$localAssetPath = OptimizeCommon::getLocalAssetPath( $finalCleanSource, $extToCheck );
if ( $localAssetPath ) {
$sourceRelPath = OptimizeCommon::getSourceRelPath( $finalCleanSource );
if ( $sourceRelPath ) {
return $finalCleanSource;
}
} else {
$finalCleanSource = str_ireplace( array( 'http://', 'https://' ), '', $finalCleanSource );
$finalCleanSource = ( strpos( $finalCleanSource, '//' ) === 0 ) ? substr( $finalCleanSource, 2 ) : $finalCleanSource; // if the string starts with '//', remove it
}
}
return $finalCleanSource;
}
/**
* @param $data
*
* @return string
*/
public static function getHardCodedManageAreaForFrontEndView($data)
{
$dataSettingsFrontEnd = ObjectCache::wpacu_cache_get('wpacu_settings_frontend_data') ?: array();
$dataSettingsFrontEnd['page_unload_text'] = esc_html($data['page_unload_text']);
// The following string will be replaced by the values got from the AJAX call to /?wpassetcleanup_load=1&wpacu_just_hardcoded
$dataWpacuSettingsFrontend = base64_encode(wp_json_encode($dataSettingsFrontEnd));
$currentHardcodedAssetRules = '';
// When the form is submitted it will clear some values if they are not sent anymore which can happen with a failed AJAX call to retrieve the list of hardcoded assets
// Place the current values to the area in case the AJAX call fails, and it won't print the list
// If the user presses "Update", it won't clear any existing rules
// If the list is printed, obviously it will be with all the fields in place as they should be
foreach (array('current_unloaded_page_level', 'load_exceptions', 'handle_unload_regex', 'handle_load_regex', 'handle_load_logged_in') as $ruleKey) {
foreach ( array( 'styles', 'scripts' ) as $assetType ) {
if ( isset( $dataSettingsFrontEnd[$ruleKey][ $assetType ] ) && ! empty( $dataSettingsFrontEnd[$ruleKey][$assetType] ) ) {
// Go through the values, depending on how the array is structured
// handle_unload_regex, handle_load_regex
if (in_array($ruleKey, array('handle_unload_regex', 'handle_load_regex'))) {
foreach ( $dataSettingsFrontEnd[ $ruleKey ][ $assetType ] as $assetHandle => $assetValues ) {
if ( strpos( $assetHandle, 'wpacu_hardcoded_' ) !== false ) {
if ($ruleKey === 'handle_unload_regex') {
$enableValue = isset( $assetValues['enable'] ) ? $assetValues['enable'] : '';
$regExValue = isset( $assetValues['value'] ) ? $assetValues['value'] : '';
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_handle_unload_regex[' . $assetType . '][' . $assetHandle . '][enable]" value="' . $enableValue . '" />';
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_handle_unload_regex[' . $assetType . '][' . $assetHandle . '][value]" value="' . esc_attr( $regExValue ) . '" />';
} elseif ($ruleKey === 'handle_load_regex') {
$enableValue = isset( $assetValues['enable'] ) ? $assetValues['enable'] : '';
$regExValue = isset( $assetValues['value'] ) ? $assetValues['value'] : '';
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_handle_load_regex[' . $assetType . '][' . $assetHandle . '][enable]" value="' . $enableValue . '" />';
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_handle_load_regex[' . $assetType . '][' . $assetHandle . '][value]" value="' . esc_attr( $regExValue ) . '" />';
}
}
}
} else {
// current unloaded on a page level, load_exceptions, handle_load_logged_in
foreach ( $dataSettingsFrontEnd[ $ruleKey ][ $assetType ] as $assetHandle ) {
if ( strpos( $assetHandle, 'wpacu_hardcoded_' ) !== false ) {
if ( $ruleKey === 'current_unloaded_page_level' ) {
$currentHardcodedAssetRules .= '<input type="hidden" name="wpassetcleanup[' . $assetType . '][]" value="' . $assetHandle . '" />';
} elseif ( $ruleKey === 'load_exceptions' ) {
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_styles_load_it[]" value="' . $assetHandle . '" />';
} elseif ($ruleKey === 'handle_load_logged_in') {
$currentHardcodedAssetRules .= '<input type="hidden" name="wpacu_load_it_logged_in['.$assetType.']['.$assetHandle.']" value="1" />';
}
}
}
}
}
}
}
return '<div class="wpacu-assets-collapsible-wrap wpacu-wrap-area wpacu-hardcoded" id="wpacu-assets-collapsible-wrap-hardcoded-list" data-wpacu-settings-frontend="'.esc_attr($dataWpacuSettingsFrontend).'">
<a class="wpacu-assets-collapsible wpacu-assets-collapsible-active" href="#" style="padding: 15px 15px 15px 44px;"><span class="dashicons dashicons-code-standards"></span> Hardcoded (non-enqueued) Styles &amp; Scripts</a>
<div class="wpacu-assets-collapsible-content" style="max-height: inherit;">
<div style="padding: 20px 0; margin: 0;"><img src="'.esc_url(admin_url('images/spinner.gif')).'" align="top" width="20" height="20" alt="" /> The list of hardcoded assets is fetched... Please wait...</div>
'.wp_kses($currentHardcodedAssetRules, array('input' => array('type' => array(), 'name' => array(), 'value' => array()))).'
</div>
</div>';
}
}