/** * Optimize website by aggregating css and js * */ public function process($sHtml) { JCH_DEBUG ? JchPlatformProfiler::start('Process', TRUE) : null; JCH_DEBUG ? JchPlatformProfiler::start('LoadClass') : null; loadJchOptimizeClass(array('JchOptimizeBase', 'JchOptimizeParser', 'JchOptimizeFileRetriever', 'JchOptimizeLinkBuilder', 'JchOptimizeHelper')); JCH_DEBUG ? JchPlatformProfiler::stop('LoadClass', TRUE) : null; try { $oParser = new JchOptimizeParser($this->params, $sHtml, JchOptimizeFileRetriever::getInstance()); $oLinkBuilder = new JchOptimizeLinkBuilder($oParser); $oLinkBuilder->insertJchLinks(); $oParser->runCookieLessDomain(); $oParser->lazyLoadImages(); $this->params->set('isXhtml', $oLinkBuilder->isXhtml()); $this->params->set('isHtml5', $oParser->isHtml5()); $sOptimizedHtml = JchOptimizeHelper::minifyHtml($oParser->getHtml(), $this->params); } catch (Exception $ex) { JchOptimizeLogger::log($ex->getMessage(), $this->params); $sOptimizedHtml = $sHtml; } spl_autoload_unregister('loadJchOptimizeClass'); JCH_DEBUG ? JchPlatformProfiler::stop('Process', TRUE) : null; JCH_DEBUG ? JchPlatformProfiler::attachProfiler($sOptimizedHtml) : null; if (version_compare(PHP_VERSION, '7', '>=')) { ini_set('pcre.jit', $this->jit); } return $sOptimizedHtml; }
/** * */ public function garbageCron() { JCH_DEBUG ? JchPlatformProfiler::start('GarbageCron') : null; $url = JchPlatformPaths::ajaxUrl('garbagecron'); JchOptimizeHelper::postAsync($url, $this->params, array('async' => '1')); JCH_DEBUG ? JchPlatformProfiler::stop('GarbageCron', TRUE) : null; }
/** * Optimize website by aggregating css and js * */ public function process($sHtml) { JCH_DEBUG ? JchPlatformProfiler::start('Process', TRUE) : null; JCH_DEBUG ? JchPlatformProfiler::start('LoadClass') : null; loadJchOptimizeClass(array('JchOptimizeBase', 'JchOptimizeParser', 'JchOptimizeFileRetriever', 'JchOptimizeLinkBuilder', 'JchOptimizeHelper')); JCH_DEBUG ? JchPlatformProfiler::stop('LoadClass', TRUE) : null; try { $oParser = new JchOptimizeParser($this->params, $sHtml, JchOptimizeFileRetriever::getInstance()); $oLinkBuilder = new JchOptimizeLinkBuilder($oParser); $oLinkBuilder->insertJchLinks(); $sOptimizedHtml = JchOptimizeHelper::minifyHtml($oParser->getHtml(), $this->params); } catch (Exception $ex) { JchOptimizeLogger::log($ex->getMessage(), $this->params); $sOptimizedHtml = $sHtml; } spl_autoload_unregister('loadJchOptimizeClass'); JCH_DEBUG ? JchPlatformProfiler::stop('Process', TRUE) : null; JCH_DEBUG ? JchPlatformProfiler::attachProfiler($sOptimizedHtml) : null; return $sOptimizedHtml; }
/** * * @param type $oObj * @param type $sCss * @return type */ public function generateAdminLinks($oObj, $sCss) { JCH_DEBUG ? JchPlatformProfiler::start('GenerateAdminLinks') : null; $params = clone $this->params; $params->set('javascript', '1'); $params->set('css', '1'); $params->set('excludeAllExtensions', '0'); $params->set('css_minify', '0'); $params->set('debug', '0'); $params->set('bottom_js', '2'); $sHtml = $oObj->getOriginalHtml(); $oParser = new JchOptimizeParser($params, $sHtml, JchOptimizeFileRetriever::getInstance()); $aLinks = $oParser->getReplacedFiles(); if ($sCss == '' && !empty($aLinks['css'][0])) { $oCombiner = new JchOptimizeCombiner($params, $this->bBackend); $oCssParser = new JchOptimizeCssParser($params, $this->bBackend); $oCombiner->combineFiles($aLinks['css'][0], 'css', $oCssParser); $sCss = $oCombiner->css; } $oSpriteGenerator = new JchOptimizeSpriteGenerator($params); $aLinks['images'] = $oSpriteGenerator->processCssUrls($sCss, TRUE); JCH_DEBUG ? JchPlatformProfiler::stop('GenerateAdminLinks', TRUE) : null; return $aLinks; }
/** * * @return type */ public function lazyLoadImages() { if ($this->params->get('pro_lazyload', '0')) { JCH_DEBUG ? JchPlatformProfiler::start('LazyLoadImages') : null; $sLazyLoadBodyHtml = preg_replace($this->getLazyLoadRegex(), '$1src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" data-src="$3" ' . 'data-jchll="true"$4<noscript>$1$2$4</noscript>', $this->getBodyHtml()); if (is_null($sLazyLoadBodyHtml)) { JchOptimizeLogger::log('Lazy load images function failed', $this->params); return; } if (preg_match($this->getBodyRegex(), $sLazyLoadBodyHtml, $aBodyMatches) === FALSE || empty($aBodyMatches)) { JchOptimizeLogger::log('Failed retrieving body in lazy load images function', $this->params); return; } $this->sBodyHtml = $aBodyMatches[0]; JCH_DEBUG ? JchPlatformProfiler::stop('LazyLoadImages', TRUE) : null; } }
/** * * @param type $oObj * @param type $sCss * @return type */ public function generateAdminLinks($oObj, $sCss) { JCH_DEBUG ? JchPlatformProfiler::start('GenerateAdminLinks') : null; $params = clone $this->params; $params->set('javascript', '1'); $params->set('css', '1'); $params->set('excludeAllExtensions', '0'); $params->set('css_minify', '0'); $params->set('debug', '0'); $params->set('bottom_js', '2'); ##<procode>## $params->set('pro_phpAndExternal', '1'); $params->set('pro_inlineScripts', '1'); $params->set('pro_lazyload', '0'); ##</procode>## $sHtml = $oObj->getOriginalHtml(); $oParser = new JchOptimizeParser($params, $sHtml, JchOptimizeFileRetriever::getInstance()); $aLinks = $oParser->getReplacedFiles(); if ($sCss == '' && !empty($aLinks['css'][0])) { $oCombiner = new JchOptimizeCombiner($params, $this->bBackend); $oCssParser = new JchOptimizeCssParser($params, $this->bBackend); $oCombiner->combineFiles($aLinks['css'][0], 'css', $oCssParser); $sCss = $oCombiner->css; } $oSpriteGenerator = new JchOptimizeSpriteGenerator($params); $aLinks['images'] = $oSpriteGenerator->processCssUrls($sCss, TRUE); ##<procode>## $sRegex = $oParser->getLazyLoadRegex(); preg_match_all($sRegex, $oParser->getBodyHtml(), $aMatches); $aLinks['lazyload'] = $aMatches[1]; ##</procode>## JCH_DEBUG ? JchPlatformProfiler::stop('GenerateAdminLinks', TRUE) : null; return $aLinks; }
/** * If parameter is set will minify HTML before sending to browser; * Inline CSS and JS will also be minified if respective parameters are set * * @return string Optimized HTML * @throws Exception */ public static function minifyHtml($sHtml, $oParams) { JCH_DEBUG ? JchPlatformProfiler::start('MinifyHtml') : null; if ($oParams->get('html_minify', 0)) { $aOptions = array(); if ($oParams->get('css_minify', 0)) { $aOptions['cssMinifier'] = array('JchOptimize\\CSS_Optimize', 'optimize'); } if ($oParams->get('js_minify', 0)) { $aOptions['jsMinifier'] = array('JchOptimize\\JS_Optimize', 'optimize'); } $aOptions['minifyLevel'] = $oParams->get('html_minify_level', 2); $aOptions['isXhtml'] = (bool) $oParams->get('isXhtml', false); $aOptions['isHtml5'] = (bool) $oParams->get('isHtml5', false); $sHtmlMin = HTML_Optimize::optimize($sHtml, $aOptions); if ($sHtmlMin == '') { JchOptimizeLogger::log('Error while minifying HTML', $oParams); $sHtmlMin = $sHtml; } $sHtml = $sHtmlMin; JCH_DEBUG ? JchPlatformProfiler::stop('MinifyHtml', TRUE) : null; } return $sHtml; }
/** * */ protected function getImagesWithoutAttributes() { if($this->params->get('img_attributes_enable', '0')) { JCH_DEBUG ? JchPlatformProfiler::start('GetImagesWithoutAttributes') : null; $rx = '#(?><?[^<]*+)*?\K(?:<img\s++(?!(?=(?>[^\s>]*+\s++)*?width\s*+=\s*+["\'][^\'">a-z]++[\'"])' . '(?=(?>[^\s>]*+\s++)*?height\s*+=\s*+["\'][^\'">a-z]++[\'"]))' . '(?=(?>[^\s>]*+\s++)*?src\s*+=(?:\s*+"([^">]*+)"|\s*+\'([^\'>]*+)\'|([^\s>]++)))[^>]*+>|$)#i'; preg_match_all($rx, $this->getBodyHtml(), $m, PREG_PATTERN_ORDER); $this->aLinks['img'] = array_map(function($a){return array_slice($a, 0, -1);}, $m); JCH_DEBUG ? JchPlatformProfiler::stop('GetImagesWithoutAttributes', true) : null; } }
/** * Insert url of aggregated file in html * * @param string $sNewLink Url of aggregated file */ protected function replaceLinks($sId, $sType) { JCH_DEBUG ? JchPlatformProfiler::start('ReplaceLinks - ' . $sType) : null; $sSearchArea = $this->oParser->getHeadHtml(); $sSearchArea .= $this->oParser->getBodyHtml(); $sLink = $this->{'getNew' . ucfirst($sType) . 'Link'}(); $sUrl = $this->buildUrl($sId, $sType); $sNewLink = str_replace('URL', $sUrl, $sLink); if ($sType == 'js' && $this->params->get('bottom_js', '0') == '1') { $sNewLink = str_replace('JCHI', '0', $sNewLink); $sSearchArea = str_replace('</body>', $sNewLink, $sSearchArea); $this->oParser->sHtml = str_replace('</body>', $sNewLink, $this->oParser->sHtml); } elseif ($this->params->get('bottom_js', '0')) { $sNewLink = str_replace('JCHI', '0', $sNewLink); $sSearchArea = str_replace('</title>', $sNewLink, $sSearchArea); } else { $sSearchArea = preg_replace_callback('#<JCH_' . strtoupper($sType) . '([^>]++)>#', function ($aM) use($sNewLink) { return str_replace('JCHI', $aM[1], $sNewLink); }, $sSearchArea); } $this->oParser->setSearchArea(preg_replace($this->oParser->getBodyRegex(), '', $sSearchArea, 1), 'head'); $this->oParser->setSearchArea(preg_replace($this->oParser->getHeadRegex(), '', $sSearchArea, 1), 'body'); JCH_DEBUG ? JchPlatformProfiler::stop('ReplaceLinks - ' . $sType, TRUE) : null; }
/** * Aggregate contents of CSS and JS files * * @param array $aUrlArray Array of links of files * @param string $sType CSS or js * @return string Aggregarted contents * @throws Exception */ public function combineFiles($aUrlArray, $sType, $oCssParser) { $sContents = ''; $sLifetime = (int) $this->params->get('lifetime', '30') * 24 * 60 * 60; $this->bAsync = FALSE; $this->sAsyncUrl = ''; $oFileRetriever = JchOptimizeFileRetriever::getInstance(); foreach ($aUrlArray as $aUrl) { $sUrl = $this->prepareFileUrl($aUrl, $sType); JCH_DEBUG ? JchPlatformProfiler::start('CombineFile - ' . $sUrl) : null; if ($sType == 'js') { $sJsContents = $this->handleAsyncUrls($aUrl); if ($sJsContents === FALSE) { continue; } $sContents .= $sJsContents; unset($sJsContents); } if (isset($aUrl['id']) && $aUrl['id'] != '') { $function = array($this, 'cacheContent'); $args = array($aUrl, $sType, $oFileRetriever, $oCssParser, TRUE); $sCachedContent = JchPlatformCache::getCallbackCache($aUrl['id'], $sLifetime, $function, $args); $this->{$sType} .= $sCachedContent; if (!isset($aUrl['url']) && $this->sAsyncUrl != '' && $sType == 'js') { $this->sAsyncUrl = ''; } if ($this->sAsyncUrl == '') { $sContents .= $this->addCommentedUrl($sType, $aUrl) . '[[JCH_' . $aUrl['id'] . ']]' . $this->sLnEnd . 'DELIMITER'; } } else { $sContent = $this->cacheContent($aUrl, $sType, $oFileRetriever, $oCssParser, FALSE); $sContents .= $this->addCommentedUrl($sType, $aUrl) . $sContent . '|"LINE_END"|'; } JCH_DEBUG ? JchPlatformProfiler::stop('CombineFile - ' . $sUrl, TRUE) : null; } if ($this->bAsync) { $sContents = $this->getLoadScript() . $sContents; if ($this->sAsyncUrl != '') { $sContents .= $this->addCommentedUrl('js', $this->sAsyncUrl) . 'loadScript("' . $this->sAsyncUrl . '", function(){}); DELIMITER'; $this->sAsyncUrl = ''; } } return $sContents; }
/** * * @return type */ public function lazyLoadImages() { if ($this->params->get('pro_lazyload', '0')) { JCH_DEBUG ? JchPlatformProfiler::start('LazyLoadImages') : null; $sLazyLoadBodyHtml = preg_replace($this->getLazyLoadRegex(), 'data-src="$1" src="' . JchOptimizeHelper::cookieLessDomain($this->params) . JchPlatformPaths::imageFolder() . 'placeholder.gif" data-jchll="true"', $this->getBodyHtml()); if (is_null($sLazyLoadBodyHtml)) { JchOptimizeLogger::log(JchPlatformUtility::translate('Lazy load images function failed'), $this->params); return; } if (preg_match($this->getBodyRegex(), $sLazyLoadBodyHtml, $aBodyMatches) === FALSE || empty($aBodyMatches)) { JchOptimizeLogger::log(JchPlatformUtility::translate('Failed retrieving body in lazy load images function'), $this->params); return; } $this->sBodyHtml = $aBodyMatches[0]; JCH_DEBUG ? JchPlatformProfiler::stop('LazyLoadImages', TRUE) : null; } }
/** * Uses regex to find css declarations containing background images to include in sprite * * @param string $sCss Aggregated css file * @return array Array of css declarations and image urls to replace with sprite */ public function processCssUrls($sCss, $bBackend = FALSE) { JCH_DEBUG ? JchPlatformProfiler::start('ProcessCssUrls') : null; $params = $this->params; $aRegexStart = array(); $aRegexStart[0] = ' #(?:{ (?=\\s*+(?>[^}\\s:]++[\\s:]++)*?url\\( (?=[^)]+\\.(?:png|gif|jpe?g)) ([^)]+)\\))'; $aRegexStart[1] = ' (?=\\s*+(?>[^}\\s:]++[\\s:]++)*?no-repeat)'; $aRegexStart[2] = ' (?(?=\\s*(?>[^};]++[;\\s]++)*?background(?:-position)?\\s*+:\\s*+(?:[^;}\\s]++[^}\\S]++)*? (?<p>(?:[tblrc](?:op|ottom|eft|ight|enter)|-?[0-9]+(?:%|[c-x]{2})?))(?:\\s+(?&p))?) (?=\\s*(?>[^};]++[;\\s]++)*?background(?:-position)?\\s*+:\\s*+(?>[^;}\\s]++[\\s]++)*? (?:left|top|0(?:%|[c-x]{2})?)\\s+(?:left|top|0(?:%|[c-x]{2})?) ) )'; $sRegexMiddle = ' [^{}]++} )'; $sRegexEnd = '#isx'; $aIncludeImages = JchOptimizeHelper::getArray($params->get('csg_include_images')); $aExcludeImages = JchOptimizeHelper::getArray($params->get('csg_exclude_images')); $sIncImagesRegex = ''; if (!empty($aIncludeImages[0])) { foreach ($aIncludeImages as &$sImage) { $sImage = preg_quote($sImage, '#'); } $sIncImagesRegex .= ' |(?:{ (?=\\s*+(?>[^}\\s:]++[\\s:]++)*?url'; $sIncImagesRegex .= ' (?=[^)]* [/(](?:' . implode('|', $aIncludeImages) . ')\\))'; $sIncImagesRegex .= '\\(([^)]+)\\) ) [^{}]++ })'; } $sExImagesRegex = ''; if (!empty($aExcludeImages[0])) { $sExImagesRegex .= '(?=\\s*+(?>[^}\\s:]++[\\s:]++)*?url\\( [^)]++ (?<!'; foreach ($aExcludeImages as &$sImage) { $sImage = preg_quote($sImage, '#'); } $sExImagesRegex .= implode('|', $aExcludeImages) . ')\\) )'; } $sRegexStart = implode('', $aRegexStart); $sRegex = $sRegexStart . $sExImagesRegex . $sRegexMiddle . $sIncImagesRegex . $sRegexEnd; if (preg_match_all($sRegex, $sCss, $aMatches) === FALSE) { throw new Exception('Error occurred matching for images to sprite'); } if (isset($aMatches[3])) { $total = count($aMatches[1]); for ($i = 0; $i < $total; $i++) { $aMatches[1][$i] = trim($aMatches[1][$i]) ? $aMatches[1][$i] : $aMatches[3][$i]; } } if ($bBackend) { $aImages = array(); $aImagesMatches = array(); $aImgRegex = array(); $aImgRegex[0] = $aRegexStart[0]; $aImgRegex[2] = $aRegexStart[1]; $aImgRegex[4] = $sRegexMiddle; $aImgRegex[5] = $sRegexEnd; $sImgRegex = implode('', $aImgRegex); if (preg_match_all($sImgRegex, $sCss, $aImagesMatches) === FALSE) { throw new Exception('Error occurred matching for images to include'); } $aImagesMatches[0] = array_diff($aImagesMatches[0], $aMatches[0]); $aImagesMatches[1] = array_diff($aImagesMatches[1], $aMatches[1]); $oImageLibrary = $this->getImageLibrary(); if ($oImageLibrary === FALSE) { return array(); } $oCssSpriteGen = new CssSpriteGen($oImageLibrary, $this->params, $bBackend); $aImages['include'] = $oCssSpriteGen->CreateSprite($aImagesMatches[1]); $aImages['exclude'] = $oCssSpriteGen->CreateSprite($aMatches[1]); return $aImages; } JCH_DEBUG ? JchPlatformProfiler::stop('ProcessCssUrls', TRUE) : null; return $aMatches; }
/** * * @param type $sRegex * @param type $sType * @param type $sSection * @param type $aCBArgs * @throws Exception */ protected function searchArea($sRegex, $sSection, $aCBArgs) { JCH_DEBUG ? JchPlatformProfiler::start('SearchArea - ' . $sSection) : null; $obj = $this; $sProcessedHtml = preg_replace_callback($sRegex, function ($aMatches) use($obj, $aCBArgs) { return $obj->replaceScripts($aMatches, $aCBArgs); }, $this->{'s' . ucfirst($sSection) . 'Html'}); if (is_null($sProcessedHtml)) { throw new Exception(sprintf('Error while parsing for links in %1$s', $sSection)); } $this->{'s' . ucfirst($sSection) . 'Html'} = $sProcessedHtml; JCH_DEBUG ? JchPlatformProfiler::stop('SearchArea - ' . $sSection, TRUE) : null; }
public function parseUserAgent($userAgent) { JCH_DEBUG ? JchPlatformProfiler::start('ParseUserAgent') : null; //Chrome if (preg_match('#^(?:(?=[^(]*+\\([^AM)]*+(Android|Macintosh)))?(?>(?:Mozilla|AppleWebKit|Safari)/[\\d. ]*+(?:\\([^)]*+\\)(?:[^()]*+\\))*\\s*+)?|Chrome/(\\d++\\.\\d++)[\\d. ]*+|Mobile\\s*+){4,5}$|' . '^(?=[^(]*+\\([^A)]*+Android)(?=(?>V?[^V]*+)*?Version/)(?>(?:Mozilla|AppleWebKit|Safari|Version)/[\\d. ]*+(?:\\([^)]*+\\)(?:[^()]*+\\))*\\s*+)?|Chrome/(\\d++\\.\\d++)[\\d. ]*+|Mobile\\s*+){5,6}$|' . '^(?>(?:Mozilla|AppleWebKit|Safari|Chrome)/[\\d. ]*+(?:\\([^)]*+\\)(?:[^()]*+\\))*\\s*+)?|Chromium/(\\d++\\.\\d++)[\\d. ]*+|(?:Fedora|Ubuntu)/?[\\d. ]*+){5,6}#', $userAgent, $version)) { if (!empty($version[2])) { $this->browser = 'Chrome'; $this->version = (double) $version[2]; } elseif (!empty($version[3])) { $this->browser = 'Android WebView'; $this->version = (double) $version[3]; } elseif (!empty($version[4])) { $this->browser = 'Chromium'; $this->version = (double) $version[4]; } if (!empty($version[1]) || $this->browser == 'Android WebView') { if ($this->version >= 40) { $this->fontHash = 'c0fbf0f0/woff2'; } elseif ($this->version >= 36) { $this->fontHash = 'd70f5a27/woff2'; } elseif ($this->version >= 31) { $this->fontHash = '1578596c/woff'; } else { $this->fontHash = '34c6462b/woff'; } } else { if ($this->version >= 40) { $this->fontHash = 'd858f13e/woff2'; } elseif ($this->version >= 36) { $this->fontHash = 'c9ad59db/woff2'; } else { $this->fontHash = '1dc159a2/woff'; } } } elseif (preg_match('#^(?=(?>F?[^F]*+)*?Firefox/(\\d++\\.\\d++))(?:(?=[^(]*+\\((?>[AiM]?[^AiM)]*+)*?(Android|(?:Macintosh|iP(?:[oa]d|hone)))))?(?>(?:Mozilla|MyWebkit|Gecko|Firefox|Navigator|TenFourFox)/[\\d. ]*+(?:\\([^)]*+\\)(?:[^()]*+\\))*\\s*+)?|[^/]++){3,4}$#', $userAgent, $version)) { $this->browser = 'Firefox'; $this->version = (double) $version[1]; if (!empty($version[2]) && $version[2] == 'Android') { if ($this->version >= 35) { $this->fontHash = '9a0a6e1c/woff2/woff'; } elseif ($this->version >= 3.6) { $this->fontHash = '34c6462b/woff'; } } elseif (!empty($version[2])) { if ($this->version >= 35) { $this->fontHash = '34b7815d/woff2/woff'; } elseif ($this->version >= 3.6) { $this->fontHash = '1578596c/woff'; } else { $this->fontHash = '269b5aae/ttf'; } } else { if ($this->version >= 35) { $this->fontHash = '0dc68147/woff2/woff'; } elseif ($this->version >= 3.6) { $this->fontHash = '1dc159a2/woff'; } } } elseif (preg_match('#^(?=(?>V?[^V]*+)*?Version/(\\d++\\.\\d++))(?:(?=[^(]*+\\([^O)]*+Opera\\s*+Mini/(\\d++\\.\\d++)))?(?>(?:Opera|Presto|Version)/[\\d. ]*+(?:\\([^)]*+\\)(?:[^()]*+\\))*\\s*+)?|[^/]++){3}$|' . '^(?=(?>O?[^O]*+)*?OPR/(\\d++\\.\\d++))(?:(?=[^(]*+\\([^M)]*+(Macintosh)))?(?>(?:Mozilla|AppleWebKit|Chrome|Safari|OPR)/[\\d. ]*+(?:\\([^)]*+\\)(?:[^()]*+\\))*\\s*+)?|[^/]++){5}$#', $userAgent, $version)) { if (!empty($version[2])) { $this->browser = 'Opera Mini'; $this->version = (double) $version[2]; } else { $this->browser = 'Opera'; $this->version = (double) (!empty($version[1]) ? $version[1] : $version[3]); } if (!empty($version[4]) && $version[4] == 'Macintosh') { if ($this->version >= 23) { $this->fontHash = 'c0fbf0f0/woff2'; } elseif ($this->version >= 11.1) { $this->fontHash = '1578596c/woff'; } else { $this->fontHash = '269b5aae/ttf'; } } else { if ($this->version >= 23) { $this->fontHash = 'd858f13e/woff2'; } elseif ($this->version >= 11.1) { $this->fontHash = '1dc159a2/woff'; } } } elseif (preg_match('#^(?:(?=[^(]*+\\(\\s*+(?:Macintosh|iP(?:[oa]d|hone))[^O)]++OS\\s*+X?\\s*+(\\d++[_.]\\d*+)))?(?:(?=(?>[CV]?[^CV]*+)*?(CriOS|Version)/(\\d++\\.\\d++)))?(?>(?:Mozilla|AppleWebKit|Version|Mobile|Safari|CriOS|OPiOS)/[\\d.\\w]*+\\s*+(?:\\([^)]*+\\)(?:[^()]*+\\))*\\s*+)?){4,5}$#', $userAgent, $version)) { if (!empty($version[2]) && $version[2] == 'CriOS') { $this->browser = 'Chrome'; } else { $this->browser = 'Safari'; } if (!empty($version[3])) { $this->version = (double) $version[3]; } if (!empty($version[1])) { $os_version = (double) str_replace('_', '.', $version[1]); // OS version if ($this->version >= 5 || $this->version == 0.0) { $this->fontHash = '1578596c/woff'; } elseif ($this->version < 5 && $os_version <= 4.2) { $this->fontHash = 'd7745f9b/svg'; } elseif ($this->version < 5 && $os_version > 4.2) { $this->fontHash = '269b5aae/ttf'; } } } elseif (preg_match('#^Mozilla/5\\.0\\s*+\\(\\s*+Windows\\s++(?:NT|Phone)[^T)]*+Trident[^r)]*+rv:(\\d++)\\.[^)]*+\\)\\s*+like\\s++Gecko\\s*+|' . '^Mozilla/[\\d. ]*+\\(compatible;\\s*+MSIE (\\d++)\\.#', $userAgent, $version)) { $this->browser = 'IE'; $this->version = (double) (!empty($version[1]) ? $version[1] : $version[2]); if ($this->version >= 9) { $this->fontHash = '1dc159a2/woff'; } else { $this->fontHash = 'ea09403b/eot'; } } elseif (preg_match('#^(?>(?:Mozilla|AppleWebKit|Safari|Chrome)/[\\d. ]*+(?:\\([^)]*+\\)(?:[^()]*+\\))*\\s*+)?)++Edge/(\\d++\\.\\d++)[\\d. ]*+#', $userAgent, $version)) { $this->browser = 'Edge'; $this->version = (double) (!empty($version[1]) ? $version[1] : $this->version); $this->fontHash = '1dc159a2/woff'; } JCH_DEBUG ? JchPlatformProfiler::stop('ParseUserAgent', TRUE) : null; }
/** * * @staticvar string $sCssRuleRegex * @param type $sContent * @param type $sHtml */ public function optimizeCssDelivery($sContents, $sHtml) { JCH_DEBUG ? JchPlatformProfiler::start('OptimizeCssDelivery') : null; $sHtmlTruncated = ''; preg_replace_callback('#<(?:[a-z0-9]++)(?:[^>]*+)>(?><?[^<]*+)*?(?=<[a-z0-9])#i', function ($aM) use(&$sHtmlTruncated) { $sHtmlTruncated .= $aM[0]; return; }, $sHtml, (int) $this->params->get('pro_optimizeCssDelivery', '200')); $sHtmlTruncated = preg_replace('#\\s*=\\s*["\']([^"\']++)["\']#i', '=" $1 "', $sHtmlTruncated); $sHtmlTruncated = preg_replace_callback('#(<(?>[^<>]++|(?1))*+>)|((?<=>)(?=[^<>\\S]*+[^<>\\s])[^<>]++)#', function ($m) { if (isset($m[1]) && $m[1] != '') { return $m[0]; } if (isset($m[2]) && $m[2] != '') { return ' '; } }, $sHtmlTruncated); $obj = $this; $oDom = new DOMDocument(); libxml_use_internal_errors(TRUE); $oDom->loadHtml($sHtmlTruncated); libxml_clear_errors(); $oXPath = new DOMXPath($oDom); $sCriticalCss = ''; $sContents = preg_replace_callback('#' . self::cssRulesRegex() . '#', function ($aMatches) use($obj, $oXPath, $sHtmlTruncated, &$sCriticalCss) { return $obj->extractCriticalCss($aMatches, $oXPath, $sHtmlTruncated, $sCriticalCss); }, $sContents); $sCriticalCss = preg_replace('#@[^{]*+{[^\\S}]*+}#', '', $sCriticalCss); $sCriticalCss = preg_replace('#@[^{]*+{[^\\S}]*+}#', '', $sCriticalCss); $sCriticalCss = preg_replace('#/\\*\\*\\*!+[^!]+!\\*\\*\\*+/[^\\S]*+(?=\\/\\*\\*\\*!|$)#', '', $sCriticalCss); $sCriticalCss = preg_replace('#\\s*[\\r\\n]{2,}\\s*#', "\n\n", $sCriticalCss); $aContents = array('font-face' => trim($sContents), 'criticalcss' => trim($sCriticalCss)); JCH_DEBUG ? JchPlatformProfiler::stop('OptimizeCssDelivery', TRUE) : null; return $aContents; }