09b52d43 by Jeff Balicki

ddd

1 parent 4c19e6fe
......@@ -6,7 +6,7 @@ Description: Improve your speed score on GTmetrix, Pingdom Tools and Google Page
Author: Raul Peixoto
Author URI: http://fastvelocity.com
Text Domain: fast-velocity-minify
Version: 3.2.2
Version: 3.2.6
License: GPL2
------------------------------------------------------------------------
......
......@@ -83,6 +83,49 @@ function fvm_check_misconfiguration() {
}
}
# check if our tables exist, and do maintenance once a day
$fvm_table_checker = get_transient('fvm_table_checker');
$fvm_table_checker = false;
if ($fvm_table_checker === false) {
# test if at least one table exists
global $wpdb;
if(!is_null($wpdb)) {
$sqla_table_name = $wpdb->prefix . 'fvm_cache';
if (!$wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $sqla_table_name)) === $sqla_table_name) {
fvm_plugin_activate();
}
}
# daily maintenance
try {
if(!is_null($wpdb)) {
# limit cache table to 20k records
$lim = 20000;
$res = $wpdb->get_row("SELECT MAX(id) as maxid FROM ".$wpdb->prefix."fvm_cache");
if(isset($res->maxid) && intval($res->maxid) > $lim) {
$wpdb->query($wpdb->prepare("DELETE FROM ".$wpdb->prefix."fvm_cache WHERE id < %d LIMIT 1", (intval($res->maxid) - $lim)));
}
# limit logs table to 500 records
$lim = 500;
$res = $wpdb->get_row("SELECT MAX(id) as maxid FROM ".$wpdb->prefix."fvm_logs");
if(isset($res->maxid) && intval($res->maxid) > $lim) {
$wpdb->query($wpdb->prepare("DELETE FROM ".$wpdb->prefix."fvm_logs WHERE id < %d LIMIT 1", (intval($res->maxid) - $lim)));
}
}
} catch (Exception $e) {
error_log('Error: '.$e->getMessage(), 0);
}
}
} catch (Exception $e) {
error_log('Caught exception (fvm_initialize_database): '.$e->getMessage(), 0);
......@@ -207,25 +250,30 @@ function fvm_add_admin_menu() {
# print admin notices when needed (json)
function fvm_show_admin_notice_from_transient() {
if(current_user_can('manage_options')) {
$inf = get_transient('fvm_admin_notice');
$inf = get_transient('fvm_admin_notice_'.get_current_user_id());
# show transient after the redirect
if($inf != false && !empty($inf)) {
$jsonarr = json_decode($inf, true);
if(!is_null($jsonarr) && is_array($jsonarr)){
# add all
$jsonarr = array_unique($jsonarr);
foreach ($jsonarr as $notice) {
add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', 'FVM: '.$notice, 'info' );
}
$notices = json_decode($inf, true);
if(!is_null($notices) && is_array($notices)){
# output on other pages
if(!isset($_GET['page']) || (isset($_GET['page']) && $_GET['page'] != 'fvm')) {
settings_errors( 'fvm_admin_notice' );
# consolidate messages
$notices = array_unique($notices);
if(count($notices) > 1) {
$msg = '<div class="fvm-info-list"><h3>FVM</h3><ul>';
foreach ($notices as $notice) { $msg.= "<li>$notice</li>"; }
$msg.= '</ul></div>';
add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', $msg, 'info' );
} else {
$msg = 'FVM: '.implode(PHP_EOL, $notices);
add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', $msg, 'info' );
}
}
# remove
delete_transient('fvm_admin_notice');
# delete
delete_transient('fvm_admin_notice_'.get_current_user_id());
}
}
}
......@@ -301,47 +349,66 @@ function fvm_get_logs_callback() {
register_activation_hook($fvm_var_file, 'fvm_plugin_activate');
function fvm_plugin_activate() {
# defauls
global $wpdb;
if(is_null($wpdb)) { return false; }
# defauls
$sql = array();
$wpdb_collate = $wpdb->collate;
# create cache table
$charset_collate = $wpdb->get_charset_collate();
$sqla_table_name = $wpdb->prefix . 'fvm_cache';
$sqla = "CREATE TABLE IF NOT EXISTS {$sqla_table_name} (
`id` bigint(20) unsigned NOT NULL auto_increment ,
`uid` varchar(64) NOT NULL,
`date` bigint(10) unsigned NOT NULL,
`type` varchar(3) NOT NULL,
`content` mediumtext NOT NULL,
`meta` mediumtext NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uid (uid),
KEY date (date), KEY type (type)
)
COLLATE {$wpdb_collate}";
# create logs table
$sqlb_table_name = $wpdb->prefix . 'fvm_logs';
$sqlb = "CREATE TABLE IF NOT EXISTS {$sqlb_table_name} (
`id` bigint(20) unsigned NOT NULL auto_increment,
`uid` varchar(64) NOT NULL,
`date` bigint(10) unsigned NOT NULL,
`type` varchar(10) NOT NULL,
`msg` mediumtext NOT NULL,
`meta` mediumtext NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uid (uid),
KEY date (date),
KEY type (type)
)
COLLATE {$wpdb_collate}";
# create cache table
$sqla = "CREATE TABLE {$sqla_table_name} (
id bigint(20) unsigned NOT NULL auto_increment ,
uid varchar(64) NOT NULL,
date bigint(10) unsigned NOT NULL,
type varchar(3) NOT NULL,
content mediumtext NOT NULL,
meta mediumtext NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uid (uid),
KEY date (date), KEY type (type)
) $charset_collate;";
# create logs table
$sqlb = "CREATE TABLE {$sqlb_table_name} (
id bigint(20) unsigned NOT NULL auto_increment,
uid varchar(64) NOT NULL,
date bigint(10) unsigned NOT NULL,
type varchar(10) NOT NULL,
msg mediumtext NOT NULL,
meta mediumtext NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY uid (uid),
KEY date (date),
KEY type (type)
) $charset_collate;";
# run sql
$wpdb->query($sqla);
$wpdb->query($sqlb);
# https://developer.wordpress.org/reference/functions/dbdelta/
require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
dbDelta( $sqla );
dbDelta( $sqlb );
# test if at least one table exists
if (!$wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $sqla_table_name)) === $sqla_table_name) {
# log
$err = 'An error occurred when trying to create the database tables';
error_log($err);
# alert
if(is_admin()) {
$notices = array($err);
set_transient('fvm_admin_notice_'.get_current_user_id(), json_encode($notices), 10);
}
# try again in 1 hour
set_transient('fvm_table_checker', true, HOUR_IN_SECONDS);
} else {
# success, but check again tomorrow
set_transient('fvm_table_checker', true, DAY_IN_SECONDS);
}
}
......@@ -349,6 +416,9 @@ function fvm_plugin_activate() {
# run during deactivation
register_deactivation_hook($fvm_var_file, 'fvm_plugin_deactivate');
function fvm_plugin_deactivate() {
# process cache settings
fvm_purge_static_files();
global $wpdb;
if(is_null($wpdb)) { return false; }
......@@ -356,16 +426,17 @@ function fvm_plugin_deactivate() {
# remove options and tables
$wpdb->query("DELETE FROM {$wpdb->prefix}options WHERE option_name = 'fvm_last_cache_update'");
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_cache");
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
# process cache settings
fvm_purge_static_files();
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
}
# run during uninstall
register_uninstall_hook($fvm_var_file, 'fvm_plugin_uninstall');
function fvm_plugin_uninstall() {
# process cache settings
fvm_purge_static_files();
global $wpdb;
if(is_null($wpdb)) { return false; }
......@@ -375,9 +446,6 @@ function fvm_plugin_uninstall() {
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_cache");
$wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
# process cache settings
fvm_purge_static_files();
}
......
......@@ -214,7 +214,7 @@ function fvm_purge_others(){
do_action("swcfpc_purge_everything");
return __( 'All caches on <strong>WP Cloudflare Super Page Cache</strong> have been purged.', 'fast-velocity-minify' );
}
# Purge Hyper Cache
if (class_exists( 'HyperCache' )) {
do_action( 'autoptimize_action_cachepurged' );
......@@ -255,6 +255,30 @@ function fvm_purge_others(){
do_action('wpo_cache_flush');
return __( 'All caches on <strong>WP-Optimize</strong> have been purged.', 'fast-velocity-minify' );
}
# nginx helper
if(has_action('rt_nginx_helper_purge_all')) {
do_action('rt_nginx_helper_purge_all');
$ret[] = __( 'Nginx Helper' );
}
# Object Cache
# WordPress OPCache
if (function_exists('wp_cache_flush')) {
wp_cache_flush();
}
# Purge Redis Object Cache plugin
if(class_exists('Redis') && defined('WP_REDIS_PATH')) {
$r = new Redis();
if ($r->connect( WP_REDIS_PATH, 0 )) {
if( false !== $r->flushdb() ) {
$ret[] = __('Redis Object Cache');
}
}
}
# hosting companies
......@@ -316,15 +340,59 @@ function fvm_purge_others(){
if(function_exists('pantheon_wp_clear_edge_all')) {
pantheon_wp_clear_edge_all();
}
# cloudways varnish
if(fvm_purge_varnish_cloudways()) {
$ret[] = __('Cloudways (Varnish)');
}
# wordpress default cache
if (function_exists('wp_cache_flush')) {
wp_cache_flush();
# bigscoots.com
if(has_action('bs_cache_purge_cache')) {
do_action('bs_cache_purge_cache');
$ret[] = __( 'BigScoots' );
}
# godaddy.com managed WordPress
if (class_exists('WPass') && method_exists('WPass\Cache', 'do_ban')){
WPaaS\Cache::do_ban();
$ret[] = __( 'GoDaddy' );
}
}
# purge varnish on cloudways
function fvm_purge_varnish_cloudways() {
# cloudways detection
if (!isset($_SERVER['cw_allowed_ip'])){ return false; }
# must have
if (!isset($_SERVER['HTTP_X_VARNISH']) || !isset($_SERVER['HTTP_X_APPLICATION'])){ return false; }
if (is_null($_SERVER['HTTP_X_VARNISH']) || is_null($_SERVER['HTTP_X_APPLICATION'])){ return false; }
if ('varnishpass' === trim($_SERVER['HTTP_X_APPLICATION'])){ return false; }
if ('bypass' === trim($_SERVER['HTTP_X_APPLICATION'])){ return false; }
# host and uri path
$host = wpraiser_get_domain();
# request arguments
$request_args = array('method' => 'PURGE', 'redirection' => 0, 'timeout' => 10, 'blocking' => false, 'headers' => array('Host' => $host, 'X-Purge-Method' => 'regex') );
# default host and port
$varnish_ip = '127.0.0.1';
$varnish_port = '8080';
# overwrite by constant
if(defined('FVM_VARNISH_IP') && !empty(FVM_VARNISH_IP)) { $varnish_ip = trim(FVM_VARNISH_IP); }
if(defined('FVM_VARNISH_PORT') && !empty(FVM_VARNISH_PORT)) { $varnish_port = trim(FVM_VARNISH_PORT); }
# purge async
$response = wp_remote_request('http://'.$varnish_ip.':'.$varnish_port.'/.*', $request_args);
return true;
}
# Purge Godaddy Managed WordPress Hosting (Varnish)
function fvm_godaddy_request( $method) {
$url = home_url();
......@@ -460,34 +528,20 @@ function fvm_generate_min_url($url, $tkey, $type, $code) {
$file = $ch_info['ch_dir'] . DIRECTORY_SEPARATOR . $filename;
$public = $ch_info['ch_url'] . '/' .$filename;
# wordpress functions
require_once (ABSPATH . DIRECTORY_SEPARATOR . 'wp-admin'. DIRECTORY_SEPARATOR .'includes'. DIRECTORY_SEPARATOR .'class-wp-filesystem-base.php');
require_once (ABSPATH . DIRECTORY_SEPARATOR .'wp-admin'. DIRECTORY_SEPARATOR .'includes'. DIRECTORY_SEPARATOR .'class-wp-filesystem-direct.php');
# initialize
$fileSystemDirect = new WP_Filesystem_Direct(false);
# create if doesn't exist
if(!$fileSystemDirect->exists($file) || ($fileSystemDirect->exists($file) && $fileSystemDirect->mtime($file) < $tvers)) {
$fileSystemDirect->put_contents($file, $code);
}
# return url
return $public;
# php
if(!file_exists($file) || (file_exists($file) && filemtime($file) < $tvers)) { file_put_contents($file, $code); }
if(file_exists($file)) { return $public; }
}
}
# default
return $url;
# default, fail and log
return false;
}
# check if PHP has some functions disabled
function fvm_function_available($func) {
if (ini_get('safe_mode')) return false;
......@@ -535,51 +589,80 @@ function fvm_purge_static_files() {
global $wpdb;
if(is_null($wpdb)) { return false; }
try {
$wpdb->query("TRUNCATE TABLE {$wpdb->prefix}fvm_cache");
$wpdb->query("TRUNCATE TABLE {$wpdb->prefix}fvm_logs");
# table names
$sqla_table_name = $wpdb->prefix . 'fvm_cache';
$sqlb_table_name = $wpdb->prefix . 'fvm_logs';
# test if at least one table exists and empty them
if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $sqla_table_name)) === $sqla_table_name) {
$wpdb->query("TRUNCATE TABLE {$sqla_table_name}");
$wpdb->query("TRUNCATE TABLE {$sqlb_table_name}");
}
} catch (Exception $e) {
error_log('Error: '.$e->getMessage(), 0);
}
# increment
update_option('fvm_last_cache_update', time());
$tver = time();
update_option('fvm_last_cache_update', $tver);
# check cache directory
$ch_info = fvm_get_cache_location();
if(isset($ch_info['ch_url']) && !empty($ch_info['ch_url']) && isset($ch_info['ch_dir']) && !empty($ch_info['ch_dir'])) {
if(is_dir($ch_info['ch_dir']) && is_writable($ch_info['ch_dir'])) {
# wordpress functions
require_once (ABSPATH . DIRECTORY_SEPARATOR . 'wp-admin'. DIRECTORY_SEPARATOR .'includes'. DIRECTORY_SEPARATOR .'class-wp-filesystem-base.php');
require_once (ABSPATH . DIRECTORY_SEPARATOR .'wp-admin'. DIRECTORY_SEPARATOR .'includes'. DIRECTORY_SEPARATOR .'class-wp-filesystem-direct.php');
# start
$fileSystemDirect = new WP_Filesystem_Direct(false);
# instant purge
global $fvm_settings;
if(isset($fvm_settings['cache']['min_instant_purge']) && $fvm_settings['cache']['min_instant_purge'] == true) {
$fileSystemDirect->rmdir($ch_info['ch_dir'], true);
return true;
} else {
fvm_rrmdir($ch_info['ch_dir']);
} else {
fvm_rrmdir($ch_info['ch_dir'], $tver); # 7 days
}
}
}
}
# remove all cache files
function fvm_rrmdir($path, $tver=null) {
clearstatcache();
if(is_dir($path)) {
try {
$i = new DirectoryIterator($path);
foreach($i as $f){
# older than 24h and not matching current timestamp
$list = $fileSystemDirect->dirlist($ch_info['ch_dir'], false, true);
if(is_array($list) && count($list) > 0) {
foreach($list as $k=>$arr) {
if(isset($arr['lastmodunix']) && $arr['type'] == 'f' && intval($arr['lastmodunix']) <= time()-86400) {
if(substr($arr['name'], 0, 10) !== time()) {
$fileSystemDirect->delete($ch_info['ch_dir'] . DIRECTORY_SEPARATOR . $arr['name'], false, 'f');
}
}
# 7 days older than timestamp
if(isset($tver) && !is_null($tver)) {
if($f->isFile() && $f->getMTime() <= intval($tver) - 86400 * 7) { @unlink($f->getRealPath()); }
if($f->isDir() && !$f->isDot()){
fvm_rrmdir($f->getRealPath(), $tver);
@rmdir($f->getRealPath());
}
} else {
# immediate
if($f->isFile()){ @unlink($f->getRealPath()); }
if($f->isDir() && !$f->isDot()){
fvm_rrmdir($f->getRealPath());
@rmdir($f->getRealPath());
}
}
}
}
}
} catch (Exception $e) {
return get_class($e) . ": " . $e->getMessage();
}
# self
if(is_dir($path)) { @rmdir($path); }
}
}
......@@ -950,20 +1033,13 @@ function fvm_replace_css_imports($css, $rq=null) {
# remove fonts and icons from final css
function fvm_extract_fonts($css_code) {
function fvm_extract_fonts($css_code, $url=null) {
global $fvm_settings, $fvm_urls;
$critical_fonts = array();
$mff = array();
$css_preload = array();
$css_code_ff = '';
# get list of fonts that are on the critical path and are to be left alone
if( isset($fvm_settings['css']['css_optimize_critical_fonts']) &&
!empty($fvm_settings['css']['css_optimize_critical_fonts'])) {
$critical_fonts = fvm_string_toarray($fvm_settings['css']['css_optimize_critical_fonts']);
}
# extract font faces
preg_match_all('/\@\s*font-face\s*\{([^}]+)\}/iUu', $css_code, $mff);
if(isset($mff[0]) && is_array($mff[0])) {
......@@ -976,6 +1052,50 @@ function fvm_extract_fonts($css_code) {
}
}
# add font-display swap for all font faces
# https://developers.google.com/web/updates/2016/02/font-display
$css_code_ff = preg_replace_callback('/(?:@font-face)\s*{(?<value>[^}]+)}/i',
function ($matches) {
if(stripos($matches['value'], 'font-display') !== false) {
return $matches[0];
} else {
return str_replace($matches['value'], 'font-display:swap;'.$matches['value'], $matches[0] );
}
},
$css_code_ff
);
# add font-display swap for all font faces
# https://developers.google.com/web/updates/2016/02/font-display
$css_code = preg_replace_callback('/(?:@font-face)\s*{(?<value>[^}]+)}/i',
function ($matches) {
if(stripos($matches['value'], 'font-display') !== false) {
return $matches[0];
} else {
return str_replace($matches['value'], 'font-display:swap;'.$matches['value'], $matches[0] );
}
},
$css_code
);
# remove query strings from fonts
$css_code_ff = preg_replace('/(.eot|.woff2|.woff|.ttf)+[?+](.+?)(\)|\'|\")/ui', "$1"."$3", $css_code_ff);
$css_code = preg_replace('/(.eot|.woff2|.woff|.ttf)+[?+](.+?)(\)|\'|\")/ui', "$1"."$3", $css_code);
# fix url paths css_code_ff
if(!empty($url)) {
$matches = array(); preg_match_all("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", $css_code_ff, $matches);
foreach($matches[1] as $a) { $b = trim($a); if($b != $a) { $css_code_ff = str_replace($a, $b, $css_code_ff); } }
$css_code_ff = preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"#])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css_code_ff);
}
# fix url paths css_code
if(!empty($url)) {
$matches = array(); preg_match_all("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", $css_code, $matches);
foreach($matches[1] as $a) { $b = trim($a); if($b != $a) { $css_code = str_replace($a, $b, $css_code); } }
$css_code = preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"#])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css_code);
}
# relative paths
$css_code_ff = str_replace('https://'.$fvm_urls['wp_domain'], '', $css_code_ff);
$css_code_ff = str_replace('http://'.$fvm_urls['wp_domain'], '', $css_code_ff);
......@@ -999,7 +1119,7 @@ function fvm_process_cdn($html) {
# html must be an object
if (!is_object($html)) {
$nobj = 1;
$html = str_get_html($html, true, true, 'UTF-8', false, PHP_EOL, ' ');
$html = fvm_str_get_html($html, true, true, 'UTF-8', false, PHP_EOL, ' ');
}
# default integration
......@@ -1653,32 +1773,9 @@ function fvm_can_process_common() {
return true;
}
# check if the user is logged in, and if the user role allows optimization
function fvm_user_role_processing_allowed($group) {
if(function_exists('is_user_logged_in') && function_exists('wp_get_current_user')) {
if(is_user_logged_in()) {
# get user roles
global $fvm_settings;
$user = wp_get_current_user();
$roles = (array) $user->roles;
foreach($roles as $role) {
if(isset($fvm_settings['minify'][$role]) && $fvm_settings['minify'][$role] == true) {
return true;
}
}
# disable for other logged in users by default
return false;
}
}
# allow by default
return true;
}
# check if we can process the page, minimum filters
function fvm_can_process_query_string($loc) {
function fvm_can_process_query_string() {
# host and uri path
$host = fvm_get_domain();
......@@ -1693,11 +1790,17 @@ function fvm_can_process_query_string($loc) {
# check
$qsarr = array(); parse_str($parse["query"], $qsarr);
# allowed queries by default
if(isset($qsarr['s'])) { unset($qsarr['s']); } # search
if(isset($qsarr['lang'])) { unset($qsarr['lang']); } # wpml
# if there are other queries left, bypass cache
# remove allowed query strings from the list of detected queries
if(isset($fvm_settings['settings']['qs']) && !empty($fvm_settings['settings']['qs'])) {
$arr = fvm_string_toarray($fvm_settings['settings']['qs']);
if(is_array($arr) && count($arr) > 0) {
foreach ($arr as $a) {
if(isset($qsarr[$e])) { unset($qsarr[$e]); }
}
}
}
# if there are other queries left, bypass processing
if(count($qsarr) > 0) {
return false;
}
......@@ -1707,6 +1810,29 @@ function fvm_can_process_query_string($loc) {
return true;
}
# check if the user is logged in, and if the user role allows optimization
function fvm_user_role_processing_allowed($group) {
if(function_exists('is_user_logged_in') && function_exists('wp_get_current_user')) {
if(is_user_logged_in()) {
# get user roles
global $fvm_settings;
$user = wp_get_current_user();
$roles = (array) $user->roles;
foreach($roles as $role) {
if(isset($fvm_settings['minify'][$role]) && $fvm_settings['minify'][$role] == true) {
return true;
}
}
# disable for other logged in users by default
return false;
}
}
# allow by default
return true;
}
# check if page is amp
......
......@@ -47,12 +47,13 @@ function fvm_process_page($html) {
# get html into an object
# https://simplehtmldom.sourceforge.io/manual.htm
$html_object = str_get_html($html, false, true, 'UTF-8', false, PHP_EOL, ' ');
$html_object = fvm_str_get_html($html, false, true, 'UTF-8', false, PHP_EOL, ' ');
# return early if html is not an object, or overwrite html into an object for processing
if (!is_object($html_object)) {
return $html . '<!-- simplehtmldom failed to process the html -->';
} else {
$html_src = $html;
$html = $html_object;
}
......@@ -66,13 +67,16 @@ function fvm_process_page($html) {
$htmljsheader = array();
$htmljsdefer = array();
# only error possible for now
$fvm_error = PHP_EOL . '<!-- ['.date('r').'] FVM has no write access for CSS / JS cache files under '. fvm_get_cache_location()['ch_url'] . ' -->'. PHP_EOL;
# collect all link preload headers, skip amp
if(fvm_is_amp_page() !== true) {
# skip on web stories
if(count($html->find('script[src*=cdn.ampproject.org]')) > 0) {
return $html . '<!-- FVM does not support AMP -->';
return $html_src . DIRECTORY_SEPARATOR . '<!-- FVM ['.date('r').'] does not support AMP -->';
}
# add other preloads
......@@ -188,7 +192,7 @@ function fvm_process_page($html) {
# extract fonts and icons
if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
$extract_fonts_arr = fvm_extract_fonts($css['code']);
$extract_fonts_arr = fvm_extract_fonts($css['code'], $href);
$css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
$css_code = $extract_fonts_arr['code'];
} else {
......@@ -259,7 +263,7 @@ function fvm_process_page($html) {
# extract fonts and icons
if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
$extract_fonts_arr = fvm_extract_fonts($css['code']);
$extract_fonts_arr = fvm_extract_fonts($css['code'], $href);
$css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
$css_code = $extract_fonts_arr['code'];
} else {
......@@ -277,6 +281,9 @@ function fvm_process_page($html) {
# generate url
$ind_css_url = fvm_generate_min_url($href, $css['tkey'], 'css', $css_code);
if($ind_css_url === false) {
return $html_src . $fvm_error;
}
# cdn
if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
......@@ -402,14 +409,17 @@ function fvm_process_page($html) {
# generate url
$css_fonts_url = fvm_generate_min_url('fonts', $tkey, 'css', $css_lowpriority_code);
# cdn
if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
$css_fonts_url = fvm_rewrite_cdn_url($css_fonts_url);
if($css_fonts_url === false) {
return $html_src . $fvm_error;
}
# preload
$htmlcssheader[0] = '<link id="fvm-fonts" rel="stylesheet" href="'.$css_fonts_url.'" media="fonts" onload="if(fvmuag()){this.media=\'all\'}" />';
# cdn
if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
$css_fonts_url = fvm_rewrite_cdn_url($css_fonts_url);
}
# preload
$htmlcssheader[0] = '<link id="fvm-fonts" rel="stylesheet" href="'.$css_fonts_url.'" media="fonts" onload="if(fvmuag()){this.media=\'all\'}" />';
}
# END OPTIMIZED FONT DELIVERY
......@@ -435,29 +445,32 @@ function fvm_process_page($html) {
# url, preload, add
$merged_css_url = fvm_generate_min_url('combined', $tkey, 'css', $merged_css);
# cdn
if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
$merged_css_url = fvm_rewrite_cdn_url($merged_css_url);
if($merged_css_url === false) {
return $html_src . $fvm_error;
}
# http, html preload + header
if($css_method == 'block') {
# add to header
$htmlcssheader[] = '<link rel="stylesheet" href="'.$merged_css_url.'" media="'.$mediatype.'" />';
# http and html preload for render blocking css
if(!isset($fvm_settings['css']['nopreload']) || (isset($fvm_settings['css']['nopreload']) && $fvm_settings['css']['nopreload'] != true)) {
$htmlpreloads[] = '<link rel="preload" href="'.$merged_css_url.'" as="style" media="'.$mediatype.'" />';
# cdn
if(isset($fvm_settings['cdn']['cssok']) && $fvm_settings['cdn']['cssok'] == true) {
$merged_css_url = fvm_rewrite_cdn_url($merged_css_url);
}
} else {
# http, html preload + header
if($css_method == 'block') {
# add to header
$htmlcssheader[] = '<link rel="stylesheet" href="'.$merged_css_url.'" media="'.$mediatype.'" />';
# http and html preload for render blocking css
if(!isset($fvm_settings['css']['nopreload']) || (isset($fvm_settings['css']['nopreload']) && $fvm_settings['css']['nopreload'] != true)) {
$htmlpreloads[] = '<link rel="preload" href="'.$merged_css_url.'" as="style" media="'.$mediatype.'" />';
}
} else {
# async
$htmlcssheader[] = '<link rel="preload" as="style" href="'.$merged_css_url.'" media="'.$mediatype.'" onload="this.rel=\'stylesheet\'" />';
# async
$htmlcssheader[] = '<link rel="preload" as="style" href="'.$merged_css_url.'" media="'.$mediatype.'" onload="this.rel=\'stylesheet\'" />';
}
}
}
}
......@@ -708,6 +721,9 @@ function fvm_process_page($html) {
# generate url
$ind_js_url = fvm_generate_min_url($tag->src, $js['tkey'], 'js', $js['code']);
if($ind_js_url === false) {
return $html_src . $fvm_error;
}
# cdn
if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
......@@ -759,6 +775,9 @@ function fvm_process_page($html) {
# generate url
$ind_js_url = fvm_generate_min_url($tag->src, $js['tkey'], 'js', $js['code']);
if($ind_js_url === false) {
return $html_src . $fvm_error;
}
# cdn
if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
......@@ -885,20 +904,23 @@ function fvm_process_page($html) {
# generate url
$merged_js_url = fvm_generate_min_url('combined', $tkey, 'js', $merged_js);
# cdn
if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
$merged_js_url = fvm_rewrite_cdn_url($merged_js_url);
}
# http and html preload for render blocking scripts
if(!isset($fvm_settings['js']['nopreload']) || (isset($fvm_settings['js']['nopreload']) && $fvm_settings['js']['nopreload'] != true)) {
$htmlpreloads[] = '<link rel="preload" href="'.$merged_js_url.'" as="script" />';
if($merged_js_url === false) {
return $html_src . $fvm_error;
}
# add to header
$htmljsheader[] = "<script data-cfasync='false' src='".$merged_js_url."'></script>";
# cdn
if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
$merged_js_url = fvm_rewrite_cdn_url($merged_js_url);
}
# http and html preload for render blocking scripts
if(!isset($fvm_settings['js']['nopreload']) || (isset($fvm_settings['js']['nopreload']) && $fvm_settings['js']['nopreload'] != true)) {
$htmlpreloads[] = '<link rel="preload" href="'.$merged_js_url.'" as="script" />';
}
# add to header
$htmljsheader[] = "<script data-cfasync='false' src='".$merged_js_url."'></script>";
}
# deferred scripts
......@@ -910,15 +932,18 @@ function fvm_process_page($html) {
# generate url
$merged_js_url = fvm_generate_min_url('combined', $tkey, 'js', $merged_js);
# cdn
if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
$merged_js_url = fvm_rewrite_cdn_url($merged_js_url);
if($merged_js_url === false) {
return $html_src . $fvm_error;
}
# header, no preload for deferred files
$htmljsheader[] = "<script defer='defer' src='".$merged_js_url."'></script>";
# cdn
if(isset($fvm_settings['cdn']['jsok']) && $fvm_settings['cdn']['jsok'] == true) {
$merged_js_url = fvm_rewrite_cdn_url($merged_js_url);
}
# header, no preload for deferred files
$htmljsheader[] = "<script defer='defer' src='".$merged_js_url."'></script>";
}
}
......
......@@ -286,6 +286,21 @@
</div>
<div style="height: 20px;"></div>
<h2 class="title">Query String Settings</h2>
<div class="accordion">
<h3>Allowed Query Strings</h3>
<div>
<p><strong>Notes:</strong></p>
<p>This allows processing of CSS, HTML and JS when the url contains certain query strings, but note that if there are other query strings found on the same url not on the list of allowed query strings, it will still not process the url.</p>
<p><strong>Example Settings:</strong></p>
<p class="fvm-code-full">
utm_source<br />utm_campaign<br />utm_medium<br />utm_expid<br />utm_term<br />utm_content<br />fb_action_ids<br />fb_action_types<br />fb_source<br />fbclid<br />_ga<br />gclid<br />age-verified<br />usqp<br />cn-reloaded<br />lang<br />s<br />permalink_name<br />lp-variation-id<br />author<br />author_name<br />cat<br />category_name<br />order<br />orderby<br />p<br />page_id<br />page<br />paged<br />post_type<br />posts<br />s<br />search<br />taxonomy<br />tag<br />tag_id<br />term
</p>
</div>
</div>
<div style="height: 20px;"></div>
<h2 class="title">User Settings</h2>
<div class="accordion">
......
......@@ -22,7 +22,7 @@
<fieldset>
<label for="fvm_settings_cache_min_instant_purge">
<input name="fvm_settings[cache][min_instant_purge]" type="checkbox" id="fvm_settings_cache_min_instant_purge" value="1" <?php echo fvm_get_settings_checkbox(fvm_get_settings_value($fvm_settings, 'cache', 'min_instant_purge')); ?>>
<?php _e( 'Purge Minified CSS/JS files instantly', 'fast-velocity-minify' ); ?> <span class="note-info">[ <?php _e( 'Cache files are only deleted if older than 24h by default, for compatibility with certain hosting providers.', 'fast-velocity-minify' ); ?> ]</span></label>
<?php _e( 'Purge Minified CSS/JS files instantly', 'fast-velocity-minify' ); ?> <span class="note-info">[ <?php _e( 'CSS & JS cache files are preserved for 7 days by default, for better compatibility with certain hosting providers.', 'fast-velocity-minify' ); ?> ]</span></label>
<br />
</fieldset></td>
......@@ -160,7 +160,7 @@
<td><fieldset>
<label for="fvm_settings_css_remove"><span class="fvm-bold-green fvm-rowintro"><?php _e( 'Remove the following CSS files', 'fast-velocity-minify' ); ?></span></label>
<p><textarea name="fvm_settings[css][remove]" rows="7" cols="50" id="fvm_settings_css_remove" class="large-text code" placeholder="ex: fonts.googleapis.com"><?php echo fvm_get_settings_value($fvm_settings, 'css', 'remove'); ?></textarea></p>
<p class="description">[ <?php _e( 'This will allow you to remove unwanted CSS files by URL path from the frontend', 'fast-velocity-minify' ); ?> ]</p>
<p class="description">[ <?php _e( 'This will allow you to remove unwanted CSS files by URI path from the frontend', 'fast-velocity-minify' ); ?> ]</p>
<p class="description">[ <?php _e( 'Will match using <code>PHP stripos</code> against the <code>href attribute</code> on the <code>link tag</code>', 'fast-velocity-minify' ); ?> ]</p>
</fieldset></td>
</tr>
......@@ -170,7 +170,7 @@
<td><fieldset>
<label for="fvm_settings_css_async"><span class="fvm-bold-green fvm-rowintro"><?php _e( 'Async the following CSS files', 'fast-velocity-minify' ); ?></span></label>
<p><textarea name="fvm_settings[css][async]" rows="7" cols="50" id="fvm_settings_css_async" class="large-text code" placeholder="ex: /plugins/something/assets/low-priority.css"><?php echo fvm_get_settings_value($fvm_settings, 'css', 'async'); ?></textarea></p>
<p class="description">[ <?php _e( 'This will allow you to remove unwanted CSS files by URL path from the frontend', 'fast-velocity-minify' ); ?> ]</p>
<p class="description">[ <?php _e( 'This will allow you to Async CSS files by URI path from the frontend', 'fast-velocity-minify' ); ?> ]</p>
<p class="description">[ <?php _e( 'Will match using <code>PHP stripos</code> against the <code>href attribute</code> on the <code>link tag</code>', 'fast-velocity-minify' ); ?> ]</p>
</fieldset></td>
</tr>
......@@ -364,6 +364,22 @@ www.googletagmanager.com/gtm.js"><?php echo fvm_get_settings_value($fvm_settings
<div style="height: 60px;"></div>
<h2 class="title"><?php _e( 'Query Strings', 'fast-velocity-minify' ); ?></h2>
<h3 class="fvm-bold-green"><?php _e( 'Allow processing of CSS, JS & HTML on specific query strings', 'fast-velocity-minify' ); ?></h3>
<table class="form-table fvm-settings">
<tbody>
<tr>
<th scope="row"><?php _e( 'Allowed Query Strings', 'fast-velocity-minify' ); ?></th>
<td><fieldset>
<label for="fvm_settings_settings_qs"><span class="fvm-bold-green fvm-rowintro"><?php _e( 'One query string key per line', 'fast-velocity-minify' ); ?></span></label>
<p><textarea name="fvm_settings[settings][qs]" rows="7" cols="50" id="fvm_settings_settings_qs" class="large-text code" placeholder="--- check the help section for suggestions ---"><?php echo fvm_get_settings_value($fvm_settings, 'settings', 'qs'); ?></textarea></p>
<p class="description">[ <?php _e( 'Additional query strings, keys only', 'fast-velocity-minify' ); ?> ]</p>
</fieldset></td>
</tr>
</tbody></table>
<div style="height: 60px;"></div>
<h2 class="title"><?php _e( 'User Settings', 'fast-velocity-minify' ); ?></h2>
<h3 class="fvm-bold-green"><?php _e( 'For compatibility reasons, only anonymous users should be optimized by default.', 'fast-velocity-minify' ); ?></h3>
<table class="form-table fvm-settings">
......
......@@ -563,47 +563,7 @@ class CSS extends Minify
*/
protected function shortenZeroes($content)
{
// we don't want to strip units in `calc()` expressions:
// `5px - 0px` is valid, but `5px - 0` is not
// `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
// `10 * 0` is invalid
// we've extracted calcs earlier, so we don't need to worry about this
// reusable bits of code throughout these regexes:
// before & after are used to make sure we don't match lose unintended
// 0-like values (e.g. in #000, or in http://url/1.0)
// units can be stripped from 0 values, or used to recognize non 0
// values (where wa may be able to strip a .0 suffix)
$before = '(?<=[:(, ])';
$after = '(?=[ ,);}])';
$units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
// strip units after zeroes (0px -> 0)
// NOTE: it should be safe to remove all units for a 0 value, but in
// practice, Webkit (especially Safari) seems to stumble over at least
// 0%, potentially other units as well. Only stripping 'px' for now.
// @see https://github.com/matthiasmullie/minify/issues/60
$content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
// strip 0-digits (.0 -> 0)
$content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
// strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
$content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
// strip trailing 0: 50.00 -> 50, 50.00px -> 50px
$content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
// strip leading 0: 0.1 -> .1, 01.1 -> 1.1
$content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
// strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
$content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
// IE doesn't seem to understand a unitless flex-basis value (correct -
// it goes against the spec), so let's add it in again (make it `%`,
// which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
// @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
$content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
$content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
// removed
return $content;
}
......
......@@ -26,32 +26,32 @@ if (!defined('ABSPATH')){ exit(); }
# mod
defined('FVM_MAX_FILE_SIZE') || define('FVM_MAX_FILE_SIZE', 2000000); # Process HTML up to 2 Mb
defined('DEFAULT_TARGET_CHARSET') || define('DEFAULT_TARGET_CHARSET', 'UTF-8');
defined('DEFAULT_BR_TEXT') || define('DEFAULT_BR_TEXT', "\r\n");
defined('DEFAULT_SPAN_TEXT') || define('DEFAULT_SPAN_TEXT', ' ');
defined('FVMDEFAULT_TARGET_CHARSET') || define('FVMDEFAULT_TARGET_CHARSET', 'UTF-8');
defined('FVMDEFAULT_BR_TEXT') || define('FVMDEFAULT_BR_TEXT', "\r\n");
defined('FVMDEFAULT_SPAN_TEXT') || define('FVMDEFAULT_SPAN_TEXT', ' ');
# other
define('HDOM_TYPE_ELEMENT', 1);
define('HDOM_TYPE_COMMENT', 2);
define('HDOM_TYPE_TEXT', 3);
define('HDOM_TYPE_ENDTAG', 4);
define('HDOM_TYPE_ROOT', 5);
define('HDOM_TYPE_UNKNOWN', 6);
define('HDOM_QUOTE_DOUBLE', 0);
define('HDOM_QUOTE_SINGLE', 1);
define('HDOM_QUOTE_NO', 3);
define('HDOM_INFO_BEGIN', 0);
define('HDOM_INFO_END', 1);
define('HDOM_INFO_QUOTE', 2);
define('HDOM_INFO_SPACE', 3);
define('HDOM_INFO_TEXT', 4);
define('HDOM_INFO_INNER', 5);
define('HDOM_INFO_OUTER', 6);
define('HDOM_INFO_ENDSPACE', 7);
define('HDOM_SMARTY_AS_TEXT', 1);
define('FVMHDOM_TYPE_ELEMENT', 1);
define('FVMHDOM_TYPE_COMMENT', 2);
define('FVMHDOM_TYPE_TEXT', 3);
define('FVMHDOM_TYPE_ENDTAG', 4);
define('FVMHDOM_TYPE_ROOT', 5);
define('FVMHDOM_TYPE_UNKNOWN', 6);
define('FVMHDOM_QUOTE_DOUBLE', 0);
define('FVMHDOM_QUOTE_SINGLE', 1);
define('FVMHDOM_QUOTE_NO', 3);
define('FVMHDOM_INFO_BEGIN', 0);
define('FVMHDOM_INFO_END', 1);
define('FVMHDOM_INFO_QUOTE', 2);
define('FVMHDOM_INFO_SPACE', 3);
define('FVMHDOM_INFO_TEXT', 4);
define('FVMHDOM_INFO_INNER', 5);
define('FVMHDOM_INFO_OUTER', 6);
define('FVMHDOM_INFO_ENDSPACE', 7);
define('FVMHDOM_SMARTY_AS_TEXT', 1);
# functions
function file_get_html(
function fvm_file_get_html(
$url,
$use_include_path = false,
$context = null,
......@@ -59,14 +59,14 @@ function file_get_html(
$maxLen = -1,
$lowercase = true,
$forceTagsClosed = true,
$target_charset = DEFAULT_TARGET_CHARSET,
$target_charset = FVMDEFAULT_TARGET_CHARSET,
$stripRN = true,
$defaultBRText = DEFAULT_BR_TEXT,
$defaultSpanText = DEFAULT_SPAN_TEXT)
$defaultBRText = FVMDEFAULT_BR_TEXT,
$defaultSpanText = FVMDEFAULT_SPAN_TEXT)
{
if($maxLen <= 0) { $maxLen = FVM_MAX_FILE_SIZE; }
$dom = new simple_html_dom(
$dom = new fvm_simple_html_dom(
null,
$lowercase,
$forceTagsClosed,
......@@ -97,16 +97,16 @@ function file_get_html(
return $dom->load($contents, $lowercase, $stripRN);
}
function str_get_html(
function fvm_str_get_html(
$str,
$lowercase = true,
$forceTagsClosed = true,
$target_charset = DEFAULT_TARGET_CHARSET,
$target_charset = FVMDEFAULT_TARGET_CHARSET,
$stripRN = true,
$defaultBRText = DEFAULT_BR_TEXT,
$defaultSpanText = DEFAULT_SPAN_TEXT)
$defaultBRText = FVMDEFAULT_BR_TEXT,
$defaultSpanText = FVMDEFAULT_SPAN_TEXT)
{
$dom = new simple_html_dom(
$dom = new fvm_simple_html_dom(
null,
$lowercase,
$forceTagsClosed,
......@@ -124,14 +124,14 @@ function str_get_html(
return $dom->load($str, $lowercase, $stripRN);
}
function dump_html_tree($node, $show_attr = true, $deep = 0)
function fvm_dump_html_tree($node, $show_attr = true, $deep = 0)
{
$node->dump($node);
}
class simple_html_dom_node
class fvm_simple_html_dom_node
{
public $nodetype = HDOM_TYPE_TEXT;
public $nodetype = FVMHDOM_TYPE_TEXT;
public $tag = 'text';
public $attr = array();
public $children = array();
......@@ -218,10 +218,10 @@ class simple_html_dom_node
$string .= " text: ({$this->text})";
}
$string .= ' HDOM_INNER_INFO: ';
$string .= ' FVMHDOM_INNER_INFO: ';
if (isset($node->_[HDOM_INFO_INNER])) {
$string .= "'" . $node->_[HDOM_INFO_INNER] . "'";
if (isset($node->_[FVMHDOM_INFO_INNER])) {
$string .= "'" . $node->_[FVMHDOM_INFO_INNER] . "'";
} else {
$string .= ' NULL ';
}
......@@ -345,12 +345,12 @@ class simple_html_dom_node
function innertext()
{
if (isset($this->_[HDOM_INFO_INNER])) {
return $this->_[HDOM_INFO_INNER];
if (isset($this->_[FVMHDOM_INFO_INNER])) {
return $this->_[FVMHDOM_INFO_INNER];
}
if (isset($this->_[HDOM_INFO_TEXT])) {
return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
if (isset($this->_[FVMHDOM_INFO_TEXT])) {
return $this->dom->restore_noise($this->_[FVMHDOM_INFO_TEXT]);
}
$ret = '';
......@@ -387,24 +387,24 @@ class simple_html_dom_node
call_user_func_array($this->dom->callback, array($this));
}
if (isset($this->_[HDOM_INFO_OUTER])) {
return $this->_[HDOM_INFO_OUTER];
if (isset($this->_[FVMHDOM_INFO_OUTER])) {
return $this->_[FVMHDOM_INFO_OUTER];
}
if (isset($this->_[HDOM_INFO_TEXT])) {
return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
if (isset($this->_[FVMHDOM_INFO_TEXT])) {
return $this->dom->restore_noise($this->_[FVMHDOM_INFO_TEXT]);
}
$ret = '';
if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]) {
$ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
if ($this->dom && $this->dom->nodes[$this->_[FVMHDOM_INFO_BEGIN]]) {
$ret = $this->dom->nodes[$this->_[FVMHDOM_INFO_BEGIN]]->makeup();
}
if (isset($this->_[HDOM_INFO_INNER])) {
// todo: <br> should either never have HDOM_INFO_INNER or always
if (isset($this->_[FVMHDOM_INFO_INNER])) {
// todo: <br> should either never have FVMHDOM_INFO_INNER or always
if ($this->tag !== 'br') {
$ret .= $this->_[HDOM_INFO_INNER];
$ret .= $this->_[FVMHDOM_INFO_INNER];
}
} elseif ($this->nodes) {
foreach ($this->nodes as $n) {
......@@ -412,7 +412,7 @@ class simple_html_dom_node
}
}
if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END] != 0) {
if (isset($this->_[FVMHDOM_INFO_END]) && $this->_[FVMHDOM_INFO_END] != 0) {
$ret .= '</' . $this->tag . '>';
}
......@@ -421,14 +421,14 @@ class simple_html_dom_node
function text()
{
if (isset($this->_[HDOM_INFO_INNER])) {
return $this->_[HDOM_INFO_INNER];
if (isset($this->_[FVMHDOM_INFO_INNER])) {
return $this->_[FVMHDOM_INFO_INNER];
}
switch ($this->nodetype) {
case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
case HDOM_TYPE_COMMENT: return '';
case HDOM_TYPE_UNKNOWN: return '';
case FVMHDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[FVMHDOM_INFO_TEXT]);
case FVMHDOM_TYPE_COMMENT: return '';
case FVMHDOM_TYPE_UNKNOWN: return '';
}
if (strcasecmp($this->tag, 'script') === 0) { return ''; }
......@@ -436,7 +436,7 @@ class simple_html_dom_node
$ret = '';
// In rare cases, (always node type 1 or HDOM_TYPE_ELEMENT - observed
// In rare cases, (always node type 1 or FVMHDOM_TYPE_ELEMENT - observed
// for some span tags, and some p tags) $this->nodes is set to NULL.
// NOTE: This indicates that there is a problem where it's set to NULL
// without a clear happening.
......@@ -472,8 +472,8 @@ class simple_html_dom_node
function makeup()
{
// text, comment, unknown
if (isset($this->_[HDOM_INFO_TEXT])) {
return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
if (isset($this->_[FVMHDOM_INFO_TEXT])) {
return $this->dom->restore_noise($this->_[FVMHDOM_INFO_TEXT]);
}
$ret = '<' . $this->tag;
......@@ -485,23 +485,23 @@ class simple_html_dom_node
// skip removed attribute
if ($val === null || $val === false) { continue; }
$ret .= $this->_[HDOM_INFO_SPACE][$i][0];
$ret .= $this->_[FVMHDOM_INFO_SPACE][$i][0];
//no value attr: nowrap, checked selected...
if ($val === true) {
$ret .= $key;
} else {
switch ($this->_[HDOM_INFO_QUOTE][$i])
switch ($this->_[FVMHDOM_INFO_QUOTE][$i])
{
case HDOM_QUOTE_DOUBLE: $quote = '"'; break;
case HDOM_QUOTE_SINGLE: $quote = '\''; break;
case FVMHDOM_QUOTE_DOUBLE: $quote = '"'; break;
case FVMHDOM_QUOTE_SINGLE: $quote = '\''; break;
default: $quote = '';
}
$ret .= $key
. $this->_[HDOM_INFO_SPACE][$i][1]
. $this->_[FVMHDOM_INFO_SPACE][$i][1]
. '='
. $this->_[HDOM_INFO_SPACE][$i][2]
. $this->_[FVMHDOM_INFO_SPACE][$i][2]
. $quote
. $val
. $quote;
......@@ -509,7 +509,7 @@ class simple_html_dom_node
}
$ret = $this->dom->restore_noise($ret);
return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
return $ret . $this->_[FVMHDOM_INFO_ENDSPACE] . '>';
}
function find($selector, $idx = null, $lowercase = false)
......@@ -524,9 +524,9 @@ class simple_html_dom_node
// code tracker id 2788009
// used to be: if (($levle=count($selectors[0]))===0) return array();
if (($levle = count($selectors[$c])) === 0) { return array(); }
if (!isset($this->_[HDOM_INFO_BEGIN])) { return array(); }
if (!isset($this->_[FVMHDOM_INFO_BEGIN])) { return array(); }
$head = array($this->_[HDOM_INFO_BEGIN] => 1);
$head = array($this->_[FVMHDOM_INFO_BEGIN] => 1);
$cmd = ' '; // Combinator
// handle descendant selectors, no recursive!
......@@ -575,18 +575,18 @@ class simple_html_dom_node
if ($parent_cmd === ' ') { // Descendant Combinator
// Find parent closing tag if the current element doesn't have a closing
// tag (i.e. void element)
$end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0;
$end = (!empty($this->_[FVMHDOM_INFO_END])) ? $this->_[FVMHDOM_INFO_END] : 0;
if ($end == 0) {
$parent = $this->parent;
while (!isset($parent->_[HDOM_INFO_END]) && $parent !== null) {
while (!isset($parent->_[FVMHDOM_INFO_END]) && $parent !== null) {
$end -= 1;
$parent = $parent->parent;
}
$end += $parent->_[HDOM_INFO_END];
$end += $parent->_[FVMHDOM_INFO_END];
}
// Get list of target nodes
$nodes_start = $this->_[HDOM_INFO_BEGIN] + 1;
$nodes_start = $this->_[FVMHDOM_INFO_BEGIN] + 1;
$nodes_count = $end - $nodes_start;
$nodes = array_slice($this->dom->nodes, $nodes_start, $nodes_count, true);
} elseif ($parent_cmd === '>') { // Child Combinator
......@@ -777,7 +777,7 @@ class simple_html_dom_node
}
// Found a match. Add to list and clear node
if ($pass) $ret[$node->_[HDOM_INFO_BEGIN]] = 1;
if ($pass) $ret[$node->_[FVMHDOM_INFO_BEGIN]] = 1;
unset($node);
}
// It's passed by reference so this is actually what this function returns.
......@@ -990,17 +990,17 @@ class simple_html_dom_node
if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
switch ($name) {
case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value;
case 'outertext': return $this->_[FVMHDOM_INFO_OUTER] = $value;
case 'innertext':
if (isset($this->_[HDOM_INFO_TEXT])) {
return $this->_[HDOM_INFO_TEXT] = $value;
if (isset($this->_[FVMHDOM_INFO_TEXT])) {
return $this->_[FVMHDOM_INFO_TEXT] = $value;
}
return $this->_[HDOM_INFO_INNER] = $value;
return $this->_[FVMHDOM_INFO_INNER] = $value;
}
if (!isset($this->attr[$name])) {
$this->_[HDOM_INFO_SPACE][] = array(' ', '', '');
$this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
$this->_[FVMHDOM_INFO_SPACE][] = array(' ', '', '');
$this->_[FVMHDOM_INFO_QUOTE][] = FVMHDOM_QUOTE_DOUBLE;
}
$this->attr[$name] = $value;
......@@ -1391,7 +1391,7 @@ class simple_html_dom_node
}
class simple_html_dom
class fvm_simple_html_dom
{
public $root = null;
public $nodes = array();
......@@ -1467,10 +1467,10 @@ class simple_html_dom
$str = null,
$lowercase = true,
$forceTagsClosed = true,
$target_charset = DEFAULT_TARGET_CHARSET,
$target_charset = FVMDEFAULT_TARGET_CHARSET,
$stripRN = true,
$defaultBRText = DEFAULT_BR_TEXT,
$defaultSpanText = DEFAULT_SPAN_TEXT,
$defaultBRText = FVMDEFAULT_BR_TEXT,
$defaultSpanText = FVMDEFAULT_SPAN_TEXT,
$options = 0)
{
if ($str) {
......@@ -1505,8 +1505,8 @@ class simple_html_dom
$str,
$lowercase = true,
$stripRN = true,
$defaultBRText = DEFAULT_BR_TEXT,
$defaultSpanText = DEFAULT_SPAN_TEXT,
$defaultBRText = FVMDEFAULT_BR_TEXT,
$defaultSpanText = FVMDEFAULT_SPAN_TEXT,
$options = 0)
{
global $debug_object;
......@@ -1541,14 +1541,14 @@ class simple_html_dom
// strip out server side scripts
$this->remove_noise("'(<\?)(.*?)(\?>)'s", true);
if($options & HDOM_SMARTY_AS_TEXT) { // Strip Smarty scripts
if($options & FVMHDOM_SMARTY_AS_TEXT) { // Strip Smarty scripts
$this->remove_noise("'(\{\w)(.*?)(\})'s", true);
}
// parsing
$this->parse();
// end
$this->root->_[HDOM_INFO_END] = $this->cursor;
$this->root->_[FVMHDOM_INFO_END] = $this->cursor;
$this->parse_charset();
// make load function chainable
......@@ -1628,8 +1628,8 @@ class simple_html_dom
protected function prepare(
$str, $lowercase = true,
$defaultBRText = DEFAULT_BR_TEXT,
$defaultSpanText = DEFAULT_SPAN_TEXT)
$defaultBRText = FVMDEFAULT_BR_TEXT,
$defaultSpanText = FVMDEFAULT_SPAN_TEXT)
{
$this->clear();
......@@ -1643,10 +1643,10 @@ class simple_html_dom
$this->lowercase = $lowercase;
$this->default_br_text = $defaultBRText;
$this->default_span_text = $defaultSpanText;
$this->root = new simple_html_dom_node($this);
$this->root = new fvm_simple_html_dom_node($this);
$this->root->tag = 'root';
$this->root->_[HDOM_INFO_BEGIN] = -1;
$this->root->nodetype = HDOM_TYPE_ROOT;
$this->root->_[FVMHDOM_INFO_BEGIN] = -1;
$this->root->nodetype = FVMHDOM_TYPE_ROOT;
$this->parent = $this->root;
if ($this->size > 0) { $this->char = $this->doc[0]; }
}
......@@ -1665,9 +1665,9 @@ class simple_html_dom
}
// Add a text node for text between tags
$node = new simple_html_dom_node($this);
$node = new fvm_simple_html_dom_node($this);
++$this->cursor;
$node->_[HDOM_INFO_TEXT] = $s;
$node->_[FVMHDOM_INFO_TEXT] = $s;
$this->link_nodes($node, false);
}
}
......@@ -1814,7 +1814,7 @@ class simple_html_dom
{
// Set end position if no further tags found
if ($this->char !== '<') {
$this->root->_[HDOM_INFO_END] = $this->cursor;
$this->root->_[FVMHDOM_INFO_END] = $this->cursor;
return false;
}
......@@ -1845,7 +1845,7 @@ class simple_html_dom
if (isset($this->optional_closing_tags[$parent_lower])
&& isset($this->block_tags[$tag_lower])) {
$this->parent->_[HDOM_INFO_END] = 0;
$this->parent->_[FVMHDOM_INFO_END] = 0;
$org_parent = $this->parent;
// Traverse ancestors to find a matching opening tag
......@@ -1864,7 +1864,7 @@ class simple_html_dom
$this->parent = $this->parent->parent;
}
$this->parent->_[HDOM_INFO_END] = $this->cursor;
$this->parent->_[FVMHDOM_INFO_END] = $this->cursor;
return $this->as_text_node($tag);
}
} elseif (($this->parent->parent)
......@@ -1872,7 +1872,7 @@ class simple_html_dom
) {
// Grandparent exists and current tag is a block tag, so our
// parent doesn't have an end tag
$this->parent->_[HDOM_INFO_END] = 0; // No end tag
$this->parent->_[FVMHDOM_INFO_END] = 0; // No end tag
$org_parent = $this->parent;
// Traverse ancestors to find a matching opening tag
......@@ -1886,13 +1886,13 @@ class simple_html_dom
// If we don't have a match add current tag as text node
if (strtolower($this->parent->tag) !== $tag_lower) {
$this->parent = $org_parent; // restore origonal parent
$this->parent->_[HDOM_INFO_END] = $this->cursor;
$this->parent->_[FVMHDOM_INFO_END] = $this->cursor;
return $this->as_text_node($tag);
}
} elseif (($this->parent->parent)
&& strtolower($this->parent->parent->tag) === $tag_lower
) { // Grandparent exists and current tag closes it
$this->parent->_[HDOM_INFO_END] = 0;
$this->parent->_[FVMHDOM_INFO_END] = 0;
$this->parent = $this->parent->parent;
} else { // Random tag, add as text node
return $this->as_text_node($tag);
......@@ -1900,7 +1900,7 @@ class simple_html_dom
}
// Set end position of parent tag to current cursor position
$this->parent->_[HDOM_INFO_END] = $this->cursor;
$this->parent->_[FVMHDOM_INFO_END] = $this->cursor;
if ($this->parent->parent) {
$this->parent = $this->parent->parent;
......@@ -1911,8 +1911,8 @@ class simple_html_dom
}
// start tag
$node = new simple_html_dom_node($this);
$node->_[HDOM_INFO_BEGIN] = $this->cursor;
$node = new fvm_simple_html_dom_node($this);
$node->_[FVMHDOM_INFO_BEGIN] = $this->cursor;
++$this->cursor;
$tag = $this->copy_until($this->token_slash); // Get tag name
$node->tag_start = $begin_tag_pos;
......@@ -1922,17 +1922,17 @@ class simple_html_dom
// <![CDATA[ ... ]]>
// <!-- Comment -->
if (isset($tag[0]) && $tag[0] === '!') {
$node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
$node->_[FVMHDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
if (isset($tag[2]) && $tag[1] === '-' && $tag[2] === '-') { // Comment ("<!--")
$node->nodetype = HDOM_TYPE_COMMENT;
$node->nodetype = FVMHDOM_TYPE_COMMENT;
$node->tag = 'comment';
} else { // Could be doctype or CDATA but we don't care
$node->nodetype = HDOM_TYPE_UNKNOWN;
$node->nodetype = FVMHDOM_TYPE_UNKNOWN;
$node->tag = 'unknown';
}
if ($this->char === '>') { $node->_[HDOM_INFO_TEXT] .= '>'; }
if ($this->char === '>') { $node->_[FVMHDOM_INFO_TEXT] .= '>'; }
$this->link_nodes($node, true);
$this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
......@@ -1943,7 +1943,7 @@ class simple_html_dom
// i.e. "<<html>"
if ($pos = strpos($tag, '<') !== false) {
$tag = '<' . substr($tag, 0, -1);
$node->_[HDOM_INFO_TEXT] = $tag;
$node->_[FVMHDOM_INFO_TEXT] = $tag;
$this->link_nodes($node, false);
$this->char = $this->doc[--$this->pos]; // prev
return true;
......@@ -1951,7 +1951,7 @@ class simple_html_dom
// Handle invalid tag names (i.e. "<html#doc>")
if (!preg_match('/^\w[\w:-]*$/', $tag)) {
$node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
$node->_[FVMHDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
// Next char is the beginning of a new tag, don't touch it.
if ($this->char === '<') {
......@@ -1960,14 +1960,14 @@ class simple_html_dom
}
// Next char closes current tag, add and be done with it.
if ($this->char === '>') { $node->_[HDOM_INFO_TEXT] .= '>'; }
if ($this->char === '>') { $node->_[FVMHDOM_INFO_TEXT] .= '>'; }
$this->link_nodes($node, false);
$this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
return true;
}
// begin tag, add new node
$node->nodetype = HDOM_TYPE_ELEMENT;
$node->nodetype = FVMHDOM_TYPE_ELEMENT;
$tag_lower = strtolower($tag);
$node->tag = ($this->lowercase) ? $tag_lower : $tag;
......@@ -1975,7 +1975,7 @@ class simple_html_dom
if (isset($this->optional_closing_tags[$tag_lower])) {
// Traverse ancestors to close all optional closing tags
while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)])) {
$this->parent->_[HDOM_INFO_END] = 0;
$this->parent->_[FVMHDOM_INFO_END] = 0;
$this->parent = $this->parent->parent;
}
$node->parent = $this->parent;
......@@ -2005,9 +2005,9 @@ class simple_html_dom
// handle endless '<'
// Out of bounds before the tag ended
if ($this->pos >= $this->size - 1 && $this->char !== '>') {
$node->nodetype = HDOM_TYPE_TEXT;
$node->_[HDOM_INFO_END] = 0;
$node->_[HDOM_INFO_TEXT] = '<' . $tag . $space[0] . $name;
$node->nodetype = FVMHDOM_TYPE_TEXT;
$node->_[FVMHDOM_INFO_END] = 0;
$node->_[FVMHDOM_INFO_TEXT] = '<' . $tag . $space[0] . $name;
$node->tag = 'text';
$this->link_nodes($node, false);
return true;
......@@ -2016,11 +2016,11 @@ class simple_html_dom
// handle mismatch '<'
// Attributes cannot start after opening tag
if ($this->doc[$this->pos - 1] == '<') {
$node->nodetype = HDOM_TYPE_TEXT;
$node->nodetype = FVMHDOM_TYPE_TEXT;
$node->tag = 'text';
$node->attr = array();
$node->_[HDOM_INFO_END] = 0;
$node->_[HDOM_INFO_TEXT] = substr(
$node->_[FVMHDOM_INFO_END] = 0;
$node->_[FVMHDOM_INFO_TEXT] = substr(
$this->doc,
$begin_tag_pos,
$this->pos - $begin_tag_pos - 1
......@@ -2044,12 +2044,12 @@ class simple_html_dom
$this->parse_attr($node, $name, $space); // get attribute value
} else {
//no value attr: nowrap, checked selected...
$node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
$node->_[FVMHDOM_INFO_QUOTE][] = FVMHDOM_QUOTE_NO;
$node->attr[$name] = true;
if ($this->char != '>') { $this->char = $this->doc[--$this->pos]; } // prev
}
$node->_[HDOM_INFO_SPACE][] = $space;
$node->_[FVMHDOM_INFO_SPACE][] = $space;
// prepare for next attribute
$space = array(
......@@ -2063,12 +2063,12 @@ class simple_html_dom
} while ($this->char !== '>' && $this->char !== '/'); // go until the tag ended
$this->link_nodes($node, true);
$node->_[HDOM_INFO_ENDSPACE] = $space[0];
$node->_[FVMHDOM_INFO_ENDSPACE] = $space[0];
// handle empty tags (i.e. "<div/>")
if ($this->copy_until_char('>') === '/') {
$node->_[HDOM_INFO_ENDSPACE] .= '/';
$node->_[HDOM_INFO_END] = 0;
$node->_[FVMHDOM_INFO_ENDSPACE] .= '/';
$node->_[FVMHDOM_INFO_END] = 0;
} else {
// reset parent
if (!isset($this->self_closing_tags[strtolower($node->tag)])) {
......@@ -2082,7 +2082,7 @@ class simple_html_dom
// This way when we see it in plaintext, we can generate formatting that the user wants.
// since a br tag never has sub nodes, this works well.
if ($node->tag === 'br') {
$node->_[HDOM_INFO_INNER] = $this->default_br_text;
$node->_[FVMHDOM_INFO_INNER] = $this->default_br_text;
}
return true;
......@@ -2097,19 +2097,19 @@ class simple_html_dom
switch ($this->char) {
case '"':
$quote_type = HDOM_QUOTE_DOUBLE;
$quote_type = FVMHDOM_QUOTE_DOUBLE;
$this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
$value = $this->copy_until_char('"');
$this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
break;
case '\'':
$quote_type = HDOM_QUOTE_SINGLE;
$quote_type = FVMHDOM_QUOTE_SINGLE;
$this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
$value = $this->copy_until_char('\'');
$this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
break;
default:
$quote_type = HDOM_QUOTE_NO;
$quote_type = FVMHDOM_QUOTE_NO;
$value = $this->copy_until($this->token_attr);
}
......@@ -2127,7 +2127,7 @@ class simple_html_dom
}
if (!$is_duplicate) {
$node->_[HDOM_INFO_QUOTE][] = $quote_type;
$node->_[FVMHDOM_INFO_QUOTE][] = $quote_type;
$node->attr[$name] = $value;
}
}
......@@ -2143,9 +2143,9 @@ class simple_html_dom
protected function as_text_node($tag)
{
$node = new simple_html_dom_node($this);
$node = new fvm_simple_html_dom_node($this);
++$this->cursor;
$node->_[HDOM_INFO_TEXT] = '</' . $tag . '>';
$node->_[FVMHDOM_INFO_TEXT] = '</' . $tag . '>';
$this->link_nodes($node, false);
$this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
return true;
......@@ -2323,12 +2323,12 @@ class simple_html_dom
function createElement($name, $value = null)
{
return @str_get_html("<$name>$value</$name>")->firstChild();
return @fvm_str_get_html("<$name>$value</$name>")->firstChild();
}
function createTextNode($value)
{
return @end(str_get_html($value)->nodes);
return @end(fvm_str_get_html($value)->nodes);
}
function getElementById($id)
......
......@@ -3,13 +3,13 @@ Contributors: Alignak
Tags: PHP Minify, Lighthouse, GTmetrix, Pingdom, Pagespeed, Merging, Minification, Optimization, Speed, Performance, FVM
Requires at least: 4.9
Requires PHP: 5.6
Stable tag: 3.2.2
Tested up to: 5.7.1
Stable tag: 3.2.6
Tested up to: 5.9.1
Text Domain: fast-velocity-minify
License: GPLv3 or later
License URI: http://www.gnu.org/licenses/gpl-3.0.html
Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by adjusting your CSS and JS files (defer, async, minify, combine, etc), compressing HTML, simplifying fonts and a few more speed optimization options.
Improve your speed score on GTmetrix, Pingdom Tools and Google PageSpeed Insights by adjusting CSS and JS files (defer, async, minify, combine, etc), compressing HTML, simplifying fonts and a few more speed optimization options.
== Description ==
......@@ -49,8 +49,26 @@ You need a public directory to store and serve minified cache files. If you need
== Changelog ==
= 3.2.2 [2021.05.09] =
= 3.2.6 [2022.02.06] =
* cache purging fixes
= 3.2.5 [2022.02.01] =
* changed writing the css/js files to WP_Filesystem_Direct with a secondary fallback method
* fixed a bug when merging css/js can break the site layout if the plugin failed to write the cache file (there will be an html comment on the footer if this happens)
* renamed a common name class to avoid conflicts with other plugins
= 3.2.4 [2022.01.31] =
* WP 5.9 / PHP 8 maintenance release
* changed deferred css/js cache clearing from 24h to 7 days
* added cache purging support for nginx helper plugin
* added option to allow processing on specific query strings
* other bug fixes
= 3.2.3 [2021.05.15] =
* added auto varnish cache purge for Cloudways
* switched from WP_Filesystem_Direct() to WP_Filesystem()
= 3.2.2 [2021.05.09] =
* fixed some JS files not being minified
= 3.2.1 [2021.05.07] =
......