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[] = '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]*stylesheet[^>]*(>)))|(?=(?P))#Umsi', $htmlSourceAlt, $matchesSourcesFromTags, PREG_SET_ORDER ); } if ( ! empty( $matchesSourcesFromTags ) ) { // Only the hashes are set // For instance, 'd1eae32c4e99d24573042dfbb71f5258a86e2a8e' is the hash for the following script: /* * */ $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( '', '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, '@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, '@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, ']*?>)(.*?)@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), ' $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 .= ''; $currentHardcodedAssetRules .= ''; } elseif ($ruleKey === 'handle_load_regex') { $enableValue = isset( $assetValues['enable'] ) ? $assetValues['enable'] : ''; $regExValue = isset( $assetValues['value'] ) ? $assetValues['value'] : ''; $currentHardcodedAssetRules .= ''; $currentHardcodedAssetRules .= ''; } } } } 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 .= ''; } elseif ( $ruleKey === 'load_exceptions' ) { $currentHardcodedAssetRules .= ''; } elseif ($ruleKey === 'handle_load_logged_in') { $currentHardcodedAssetRules .= ''; } } } } } } } return ' Hardcoded (non-enqueued) Styles & Scripts The list of hardcoded assets is fetched... Please wait... '.wp_kses($currentHardcodedAssetRules, array('input' => array('type' => array(), 'name' => array(), 'value' => array()))).' '; } }