<?php
/*
* SPEEDYCACHE
* https://speedycache.com/
* (c) SpeedyCache Team
*/
namespace SpeedyCache;
if( !defined('SPEEDYCACHE_PRO_VERSION') ){
die('HACKING ATTEMPT!');
}
class Enhanced{
static function init(){
global $speedycache;
$speedycache->enhanced = array();
$speedycache->enhanced['html'] = '';
$speedycache->enhanced['head_html'] = '';
$speedycache->enhanced['body_html'] = '';
$speedycache->enhanced['inline_scripts'] = '';
$speedycache->enhanced['cache_speedycache_minified'] = '';
$speedycache->enhanced['cache_speedycache_minified'] = 'cache/speedycache/'.SPEEDYCACHE_SERVER_HOST. '/assets';
}
// Removes white space after </html> & \r & any white space
static function remove_trailing_html_space($content){
global $speedycache;
$content = preg_replace("/<\/html>\s+/", '</html>', $content);
$content = str_replace("\r", '', $content);
return preg_replace("/^\s+/m", '', ((string) $content));
}
static function remove_head_comments(){
global $speedycache;
$data = $speedycache->enhanced['head_html'];
$comment_list = array();
$comment_start_index = false;
for($i = 0; $i < strlen( $data ); $i++){
if(isset($data[$i-3])){
if($data[$i-3].$data[$i-2].$data[$i-1].$data[$i] == '<!--'){
if(!preg_match("/if\s+|endif\s*\]/", substr($data, $i, 17))){
$comment_start_index = $i-3;
}
}
}
if(isset($data[$i-2])){
if($comment_start_index){
if($data[$i-2].$data[$i-1].$data[$i] == '-->'){
array_push($comment_list, array('start' => $comment_start_index, 'end' => $i));
$comment_start_index = false;
}
}
}
}
if(!empty($comment_list)){
foreach(array_reverse($comment_list) as $key => $value){
$data = substr_replace($data, '', $value['start'], ($value['end'] - $value['start'] + 1));
}
$speedycache->enhanced['html'] = str_replace($speedycache->enhanced['head_html'], $data, $speedycache->enhanced['html']);
}
return $speedycache->enhanced['html'];
}
static function eliminate_newline($start_string, $end_string, $tmp_html){
$data = $tmp_html;
$list = array();
$start_index = false;
$end_index = false;
for($i = 0; $i < strlen( $data ); $i++){
if(substr($data, $i, strlen($start_string)) == $start_string){
if(!$end_index){
$start_index = $i;
}
}
if($start_index && $i > $start_index){
if(substr($data, $i, strlen($end_string)) == $end_string){
$end_index = $i + strlen($end_string) - 1;
$text = substr($data, $start_index, ($end_index - $start_index + 1));
array_push($list, array('start' => $start_index, 'end' => $end_index, 'text' => $text));
$start_index = false;
$end_index = false;
}
}
}
if(isset($list[0])){
$list = array_reverse($list);
foreach($list as $key => $value){
if(preg_match("/(<script|<style|<textarea)/i", $value['text'])){
continue;
}
//var $bodybg=$('<div id="ncf-body-bg"/>').prependTo($body);
if(preg_match("/\)\.prependTo\(/i", $value['text'])){
continue;
}
//<div class="wp_syntax" style="position:relative;">
if(preg_match("/<div\s+class\=\"wp\_syntax\"[^\>]*>/i", $value['text'])){
continue;
}
$value['text'] = preg_replace("/\s+/", " ", ((string)$value['text']));
$tmp_html = substr_replace($tmp_html, $value['text'], $value['start'], ($value['end'] - $value['start'] + 1));
}
}
return $tmp_html;
}
static function minify_inline_css($data){
global $speedycache;
$style_list = array();
$style_start_index = false;
for($i = 0; $i < strlen( $data ); $i++){
if(isset($data[$i-5])){
if(substr($data, $i - 5, 6) == '<style'){
$style_start_index = $i - 5;
}
}
if(isset($data[$i-7])){
if($style_start_index){
if(substr($data, $i - 7, 8) == '</style>'){
array_push($style_list, array('start' => $style_start_index, 'end' => $i));
$style_start_index = false;
}
}
}
}
if(!empty($style_list)){
foreach(array_reverse($style_list) as $key => $value){
// document.write('<style type="text/css">div{}</style')
$prev_20_chars = substr($data, $value['start'] - 20, 20);
if(strpos($prev_20_chars, 'document.write') !== false){
continue;
}
$inline_style = substr($data, $value['start'], ($value['end'] - $value['start'] + 1));
if(strlen($inline_style) > 15000){
$part_of_inline_style = substr($inline_style, 0, 15000);
}else{
$part_of_inline_style = $inline_style;
}
if(preg_match('/'.preg_quote($part_of_inline_style, '/').'/i', $speedycache->enhanced['inline_scripts'])){
continue;
}
if(preg_match("/<style\s+(amp-boilerplate|amp-custom)>/", $inline_style)){
continue;
}
$inline_style = \SpeedyCache\Enhanced::minify_css($inline_style);
$inline_style = preg_replace("/\/\*(.*?)\*\//s", "\n", $inline_style); //replaces comments with \n
$inline_style = preg_replace("/(<style[^\>]*>)\s+/i", "$1", $inline_style); //removes white space after <style> tag
$inline_style = preg_replace("/\s+(<\/style[^\>]*>)/i", "$1", $inline_style); //removes white space before </style> tag
$inline_style = str_replace(' type="text/css"', '', $inline_style);
$inline_style = str_replace(' type="text/css"', '', $inline_style);
$data = substr_replace($data, $inline_style, $value['start'], ($value['end'] - $value['start'] + 1));
}
}
return $data;
}
static function remove_html_comments($data){
$comment_list = array();
$comment_start_index = false;
for($i = 0; $i < strlen($data); $i++){
if(isset($data[$i-3])){
if($data[$i-3].$data[$i-2].$data[$i-1].$data[$i] == "<!--"){
if(!preg_match("/if\s+|endif\s*\]/", substr($data, $i, 17))){
$comment_start_index = $i-3;
}
}
}
if(isset($data[$i-2])){
if($comment_start_index){
if($data[$i-2].$data[$i-1].$data[$i] == '-->'){
array_push($comment_list, array('start' => $comment_start_index, 'end' => $i));
$comment_start_index = false;
}
}
}
}
if(!empty($comment_list)){
foreach(array_reverse($comment_list) as $key => $value){
if(($value['end'] - $value['start']) > 4){
$comment_html = substr($data, $value['start'], ($value['end'] - $value['start'] + 1));
if(preg_match("/google\_ad\_slot/i", $comment_html)){
}else{
$data = substr_replace($data, '', $value['start'], ($value['end'] - $value['start'] + 1));
}
}
}
}
return $data;
}
static function minify_html(&$content){
global $speedycache;
if(defined('SPEEDYCACHE_VERSION') && version_compare(SPEEDYCACHE_VERSION, '1.2.0', '<')){
return $speedycache->enhanced['html'];
}
$tmp_html = $content;
$tmp_html = self::remove_trailing_html_space($tmp_html);
$tmp_html = self::eliminate_newline('<div', '</div>', $tmp_html);
$tmp_html = self::eliminate_newline('<li', '</li>', $tmp_html);
$tmp_html = self::minify_inline_js($tmp_html);
$tmp_html = self::minify_inline_css($tmp_html);
$tmp_html = self::remove_html_comments($tmp_html);
$tag_list = 'p|div|span|img|nav|ul|li|header|a|b|i|article|section|footer|style|script|link|meta|body';
$tmp_html = preg_replace_callback("/\<(".$tag_list.")\s+[^\>\<]+\>/i", '\SpeedyCache\Enhanced::remove_spaces_in_tag', $tmp_html);
$tmp_html = preg_replace('/\h+<\//', '</', $tmp_html);
// BECAUSE of JsemÂ<span class="label">
// - need to remove spaces between > <
// - need to remove spaces between <span> Assdfdf </span>
// $tmp_html = preg_replace("/\h*\<(".$tag_list.")\s+([^\>]+)>\h*/i", "<$1 $2>", $tmp_html);
// $tmp_html = preg_replace("/\h*\<\/(".$tag_list.")>\h*/i", "</$1>", $tmp_html);
$tmp_html = preg_replace("/\s*<\/div>\s*/is", "</div>", $tmp_html);
$content = $tmp_html;
}
static function search_in_inline_scripts($content){
global $speedycache;
if(strpos($speedycache->enhanced['inline_scripts'], $content) === false){
return false;
}
return true;
}
static function remove_spaces_in_tag($matches){
if(self::search_in_inline_scripts($matches[0])){
return $matches[0];
}
/**
* Structure of this array is
* searchable => replacer
*/
$pregs_replaces = array(
'/([\"\'])\s+\/>/' => '$1/>', // <img id="1" />
'/\s+/' => ' ', // <div id="1">
'/\s+([\"\'])/' => '$1', // <div id="1 ">
'/([a-z])\=([\"\'])\s+/' => '$1=$2', // <div id=" 1"> <img src="data:image/gif;base64,R0lAICRAEAOw==" lazy="image.jpg" />
'/\h*class\=\'\'\h*/' => ' ', // <ul class="">
'/\h*class\=\"\"\h*/' => ' ', // <ul class=''>
);
foreach($pregs_replaces as $searchable => $replacer){
$matches[0] = preg_replace($searchable, $replacer, $matches[0]);
}
// <div style="">
if(!preg_match("/id\=\"ctf_/", $matches[0])){
/*
to exclude for Custom Twitter Feeds Pro Personal
<div class="ctf-item ctf-author-msdsmarine ctf-new ctf-hide-avatar ctf-retweet ctf-tc-checked" id="ctf_1323705595325800448" style="">
*/
$matches[0] = preg_replace("/\h*style\=[\"\'][\"\']\h*/", " ", $matches[0]);
}
// <div id="1" >
// <div >
$matches[0] = preg_replace("/\h+\>/", ">", $matches[0]);
// <script src='//bqcmw.js' type="text/javascript"></script>
//$matches[0] = self::remove_type_attribute_for_js($matches[0]);
return $matches[0];
}
static function remove_type_attribute_for_js($script){
if(preg_match("/src\s*\=\s*[\"\']/", $script)){
$script = preg_replace("/\stype\s*\=\s*[\'\"][^\"\']+[\'\"]/", " ", $script);
$script = preg_replace("/\s+/", " ", $script);
$script = preg_replace("/([\'\"])\s>/", "$1>", $script);
}
return $script;
}
static function remove_single_line_comments($html){
$html = preg_replace("/<!--((?:(?!-->).)+)-->/", '', $html);
$html = preg_replace("/\/\*((?:(?!\*\/).)+)\*\//", '', $html);
return $html;
}
/* CSS Part Start */
static function minify_css($source){
$data = $source;
$curl_list = array();
$curl_start_index = false;
$curl_start_count = 0;
$curl_end_count = 0;
for($i = 0; $i < strlen( $data ); $i++){
if($data[$i] == '{'){
$curl_start_count++;
if(!$curl_start_index){
$curl_start_index = $i;
}
}
if($data[$i] == '}'){
// .icon-basic-printer:before{content:"}";}
if(isset($data[$i+1]) && $data[$i+1] != "'" && $data[$i+1] != "'"){
$curl_end_count++;
}
}
if($curl_start_count && $curl_start_count == $curl_end_count){
array_push($curl_list, array('start' => $curl_start_index - 3, 'end' => $i + 3));
$curl_start_count = 0;
$curl_end_count = 0;
$curl_start_index = false;
}
}
if(!empty($curl_list)){
foreach(array_reverse($curl_list) as $key => $value){
$new_data = substr($data, $value['start'], ($value['end'] - $value['start'] + 1));
if(!preg_match("/[^\{]+\{[^\{]+\{/", $new_data)){
$new_data = preg_replace("/\s+/", " ", ((string) $new_data));
$new_data = preg_replace("/\s+}/", "}", $new_data); //removes white space before "}"
$new_data = preg_replace("/}\s+/", "} ", $new_data); //removes white space after "}"
$new_data = preg_replace("/\s*(\{|\;|\:)\s*/", "$1", $new_data);
$data = substr_replace($data, $new_data, $value['start'], ($value['end'] - $value['start'] + 1));
}else{
$first = strpos($new_data, '{');
$last = strrpos($new_data, '}');
$new_data_tmp = substr($new_data, $first+1, $last-$first-1);
$new_data_tmp = \SpeedyCache\Enhanced::minify_css($new_data_tmp);
$new_data = substr_replace($new_data, $new_data_tmp, $first+1, ($last-$first-1));
$data = substr_replace($data, $new_data, $value['start'], ($value['end'] - $value['start'] + 1));
}
}
$source = $data;
}
//@media (max-width: 767px){
$source = preg_replace("/\@media\s*\(\s*(max-width|min-width)\s*\:\s*([^\(\)\{\}\s]+)\s*\)\s*\{/", "@media($1:$2){", $source);
//@media (min-width: 768px) and (max-width: 1018px){
$source = preg_replace("/\@media\s*\(\s*(max-width|min-width)\s*\:\s*([^\(\)\{\}\s]+)\s*\)\s*and\s*\(\s*(max-width|min-width)\s*\:\s*([^\(\)\{\}\s]+)\s*\)\s*\{/", "@media($1:$2) and ($3:$4){", $source);
//@media screen and (max-width: 479px){
$source = preg_replace("/\@media\s+screen\s+and\s*\(\s*(max-width|min-width)\s*\:\s*([^\(\)\{\}\s]+)\s*\)\s*\{/", "@media screen and ($1:$2){", $source);
/*
article,
h2,
div:first-child,
.main{padding:0;}
*/
$source = preg_replace("/^([a-z0-9\_\.\-\:\>\s]+\,)\s+/im", "$1 ", $source);
return $source;
//$source = preg_replace_callback("/\s*\{((?:(?!content|\}).)+)\}\s*/", '\SpeedyCache\Enhanced::eliminate_newline_for_css'), $source);
//return $source;
}
// Regex to replace new line after \n /\s*\;(?:\s*|\n)/
//Replaces Space before and after { } ; :
static function eliminate_newline_for_css($matches){
$matches[0] = preg_replace("/\s+/", " ", ((string) $matches[0]));
$matches[0] = preg_replace("/\s*{\s*/", "{", $matches[0]);
$matches[0] = preg_replace("/\s*}\s*/", "}", $matches[0]);
$matches[0] = preg_replace("/\s*\;\s*/", ";", $matches[0]);
$matches[0] = preg_replace("/\s*\:\s*/", ":", $matches[0]);
return $matches[0]."\n";
}
static function render_blocking($html, $render_blocking_css = false){
\SpeedyCache\RenderBlocking::init($html);
return \SpeedyCache\RenderBlocking::action($render_blocking_css);
}
static function google_fonts(){
//for checking
}
static function lazy_load($content){
global $speedycache;
\SpeedyCache\LazyLoad::init();
$funcs = array(
'\SpeedyCache\LazyLoad::images',
'\SpeedyCache\LazyLoad::iframe',
'\SpeedyCache\LazyLoad::background',
'\SpeedyCache\LazyLoad::video'
);
foreach($funcs as $fn){
// if(!function_exists($fn)){
// continue;
// }
$fn_res = call_user_func_array($fn, array($content, $speedycache->enhanced['inline_scripts']));
if(empty($fn_res)){
continue;
}
$content = $fn_res;
}
return $content;
}
/* CSS Part Start */
/* Js Part Start */
// TODO:: not used anywhere
static function single_line_js($source){
$source = preg_replace("/\n/", '', $source);
return $source;
}
static function minify_js($source, $inline_js = false){
//$source = preg_replace("/\n\/\/.*/", "", $source);
//$source = preg_replace("/\/\*.*?\*\//s", "", $source);
if(preg_match("/dynamicgoogletags\.update\(\)/i", $source)){
$source = "<script>dynamicgoogletags.update();</script>";
return $source;
}
// <script>
// (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
// (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
// m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
// })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
// ga('create', 'UA-9999-9', 'auto');
// ga('send', 'pageview');
// </script>
if(preg_match("/<script[^\>]*>\s*\(function\(i,s,o,g,r,a,m\)\{i\[\'GoogleAnalyticsObject\'\]/i", $source)){
if(preg_match("/ga\(\'send\',\s*\'pageview\'\)\;\s*<\/script>/i", $source)){
$source = preg_replace("/\s+/", " ", ((string) $source));
$source = preg_replace("/\s*<(\/?)script([^\>]*)>\s*/", "<$1script$2>", $source);
return $source;
}
}
// sometimes the lines are ended with "\r" instead of "\n"
$source = str_replace("\r", "\n", $source);
$source = preg_replace("/^\s+/m", '', $source);
if(empty($inline_js)){
// // --></script> in html
//$source = preg_replace("/\n\/\/[^\n]+/", "", $source); // to remove single line comments
$source = preg_replace_callback("/\n\/\/[^\n]+/", '\SpeedyCache\Enhanced::remove_single_line_comments_from_js', $source);
}
if(!empty($inline_js)){
if(preg_match("/var\sptsTables/i", $source) && preg_match("/var\sptsBuildConst/i", $source)){
}
//to remove only CDATA from inline js
$source = preg_replace("/\/\*\s*\<\!\[CDATA\[\s*\*\//", "", $source);
$source = preg_replace("/\/\*\s*\]\]\>\s*\*\//", "", $source);
}
//<script>//alert();</script>
if(preg_match("/<script[^\>]*>\s*\/\/[^\n]*<\/script>/i", $source)){
return '';
}
$source = preg_replace_callback("/([a-z]{4,5}\:)?\/\/[^\n]*/", '\SpeedyCache\Enhanced::remove_single_line_comments_from_js', $source);
$source = preg_replace("/\}\)\;[^\S\r\n]+/",