Fast Velocity Minify - Version 3.2.4

Version Description

[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

Download this release

Release Info

Developer Alignak
Plugin Icon 128x128 Fast Velocity Minify
Version 3.2.4
Comparing to
See all releases

Code changes from version 3.2.2 to 3.2.4

fvm.php CHANGED
@@ -6,7 +6,7 @@ Description: Improve your speed score on GTmetrix, Pingdom Tools and Google Page
6
  Author: Raul Peixoto
7
  Author URI: http://fastvelocity.com
8
  Text Domain: fast-velocity-minify
9
- Version: 3.2.2
10
  License: GPL2
11
 
12
  ------------------------------------------------------------------------
6
  Author: Raul Peixoto
7
  Author URI: http://fastvelocity.com
8
  Text Domain: fast-velocity-minify
9
+ Version: 3.2.4
10
  License: GPL2
11
 
12
  ------------------------------------------------------------------------
inc/admin.php CHANGED
@@ -83,6 +83,49 @@ function fvm_check_misconfiguration() {
83
  }
84
 
85
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
 
87
  } catch (Exception $e) {
88
  error_log('Caught exception (fvm_initialize_database): '.$e->getMessage(), 0);
@@ -207,25 +250,30 @@ function fvm_add_admin_menu() {
207
  # print admin notices when needed (json)
208
  function fvm_show_admin_notice_from_transient() {
209
  if(current_user_can('manage_options')) {
210
- $inf = get_transient('fvm_admin_notice');
 
 
211
  if($inf != false && !empty($inf)) {
212
- $jsonarr = json_decode($inf, true);
213
- if(!is_null($jsonarr) && is_array($jsonarr)){
214
-
215
- # add all
216
- $jsonarr = array_unique($jsonarr);
217
- foreach ($jsonarr as $notice) {
218
- add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', 'FVM: '.$notice, 'info' );
219
- }
220
 
221
- # output on other pages
222
- if(!isset($_GET['page']) || (isset($_GET['page']) && $_GET['page'] != 'fvm')) {
223
- settings_errors( 'fvm_admin_notice' );
 
 
 
 
 
 
 
224
  }
 
225
  }
226
 
227
- # remove
228
- delete_transient('fvm_admin_notice');
 
229
  }
230
  }
231
  }
@@ -301,47 +349,66 @@ function fvm_get_logs_callback() {
301
  register_activation_hook($fvm_var_file, 'fvm_plugin_activate');
302
  function fvm_plugin_activate() {
303
 
 
304
  global $wpdb;
305
  if(is_null($wpdb)) { return false; }
306
-
307
- # defauls
308
- $sql = array();
309
- $wpdb_collate = $wpdb->collate;
310
-
311
- # create cache table
312
  $sqla_table_name = $wpdb->prefix . 'fvm_cache';
313
- $sqla = "CREATE TABLE IF NOT EXISTS {$sqla_table_name} (
314
- `id` bigint(20) unsigned NOT NULL auto_increment ,
315
- `uid` varchar(64) NOT NULL,
316
- `date` bigint(10) unsigned NOT NULL,
317
- `type` varchar(3) NOT NULL,
318
- `content` mediumtext NOT NULL,
319
- `meta` mediumtext NOT NULL,
320
- PRIMARY KEY (id),
321
- UNIQUE KEY uid (uid),
322
- KEY date (date), KEY type (type)
323
- )
324
- COLLATE {$wpdb_collate}";
325
-
326
- # create logs table
327
  $sqlb_table_name = $wpdb->prefix . 'fvm_logs';
328
- $sqlb = "CREATE TABLE IF NOT EXISTS {$sqlb_table_name} (
329
- `id` bigint(20) unsigned NOT NULL auto_increment,
330
- `uid` varchar(64) NOT NULL,
331
- `date` bigint(10) unsigned NOT NULL,
332
- `type` varchar(10) NOT NULL,
333
- `msg` mediumtext NOT NULL,
334
- `meta` mediumtext NOT NULL,
335
- PRIMARY KEY (id),
336
- UNIQUE KEY uid (uid),
337
- KEY date (date),
338
- KEY type (type)
339
- )
340
- COLLATE {$wpdb_collate}";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
  # run sql
343
- $wpdb->query($sqla);
344
- $wpdb->query($sqlb);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
345
 
346
  }
347
 
@@ -349,6 +416,9 @@ function fvm_plugin_activate() {
349
  # run during deactivation
350
  register_deactivation_hook($fvm_var_file, 'fvm_plugin_deactivate');
351
  function fvm_plugin_deactivate() {
 
 
 
352
 
353
  global $wpdb;
354
  if(is_null($wpdb)) { return false; }
@@ -356,16 +426,17 @@ function fvm_plugin_deactivate() {
356
  # remove options and tables
357
  $wpdb->query("DELETE FROM {$wpdb->prefix}options WHERE option_name = 'fvm_last_cache_update'");
358
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_cache");
359
- $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
360
-
361
- # process cache settings
362
- fvm_purge_static_files();
363
 
364
  }
365
 
366
  # run during uninstall
367
  register_uninstall_hook($fvm_var_file, 'fvm_plugin_uninstall');
368
  function fvm_plugin_uninstall() {
 
 
 
 
369
  global $wpdb;
370
  if(is_null($wpdb)) { return false; }
371
 
@@ -375,9 +446,6 @@ function fvm_plugin_uninstall() {
375
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_cache");
376
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
377
 
378
- # process cache settings
379
- fvm_purge_static_files();
380
-
381
  }
382
 
383
 
83
  }
84
 
85
  }
86
+
87
+ # check if our tables exist, and do maintenance once a day
88
+ $fvm_table_checker = get_transient('fvm_table_checker');
89
+ $fvm_table_checker = false;
90
+ if ($fvm_table_checker === false) {
91
+
92
+ # test if at least one table exists
93
+ global $wpdb;
94
+ if(!is_null($wpdb)) {
95
+ $sqla_table_name = $wpdb->prefix . 'fvm_cache';
96
+ if (!$wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $sqla_table_name)) === $sqla_table_name) {
97
+ fvm_plugin_activate();
98
+ }
99
+ }
100
+
101
+ # daily maintenance
102
+ try {
103
+
104
+ if(!is_null($wpdb)) {
105
+
106
+ # limit cache table to 20k records
107
+ $lim = 20000;
108
+ $res = $wpdb->get_row("SELECT MAX(id) as maxid FROM ".$wpdb->prefix."fvm_cache");
109
+ if(isset($res->maxid) && intval($res->maxid) > $lim) {
110
+ $wpdb->query($wpdb->prepare("DELETE FROM ".$wpdb->prefix."fvm_cache WHERE id < %d LIMIT 1", (intval($res->maxid) - $lim)));
111
+ }
112
+
113
+ # limit logs table to 500 records
114
+ $lim = 500;
115
+ $res = $wpdb->get_row("SELECT MAX(id) as maxid FROM ".$wpdb->prefix."fvm_logs");
116
+ if(isset($res->maxid) && intval($res->maxid) > $lim) {
117
+ $wpdb->query($wpdb->prepare("DELETE FROM ".$wpdb->prefix."fvm_logs WHERE id < %d LIMIT 1", (intval($res->maxid) - $lim)));
118
+ }
119
+
120
+ }
121
+
122
+ } catch (Exception $e) {
123
+ error_log('Error: '.$e->getMessage(), 0);
124
+ }
125
+
126
+
127
+ }
128
+
129
 
130
  } catch (Exception $e) {
131
  error_log('Caught exception (fvm_initialize_database): '.$e->getMessage(), 0);
250
  # print admin notices when needed (json)
251
  function fvm_show_admin_notice_from_transient() {
252
  if(current_user_can('manage_options')) {
253
+ $inf = get_transient('fvm_admin_notice_'.get_current_user_id());
254
+
255
+ # show transient after the redirect
256
  if($inf != false && !empty($inf)) {
257
+ $notices = json_decode($inf, true);
258
+ if(!is_null($notices) && is_array($notices)){
 
 
 
 
 
 
259
 
260
+ # consolidate messages
261
+ $notices = array_unique($notices);
262
+ if(count($notices) > 1) {
263
+ $msg = '<div class="fvm-info-list"><h3>FVM</h3><ul>';
264
+ foreach ($notices as $notice) { $msg.= "<li>$notice</li>"; }
265
+ $msg.= '</ul></div>';
266
+ add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', $msg, 'info' );
267
+ } else {
268
+ $msg = 'FVM: '.implode(PHP_EOL, $notices);
269
+ add_settings_error( 'fvm_admin_notice', 'fvm_admin_notice', $msg, 'info' );
270
  }
271
+
272
  }
273
 
274
+ # delete
275
+ delete_transient('fvm_admin_notice_'.get_current_user_id());
276
+
277
  }
278
  }
279
  }
349
  register_activation_hook($fvm_var_file, 'fvm_plugin_activate');
350
  function fvm_plugin_activate() {
351
 
352
+ # defauls
353
  global $wpdb;
354
  if(is_null($wpdb)) { return false; }
355
+ $charset_collate = $wpdb->get_charset_collate();
 
 
 
 
 
356
  $sqla_table_name = $wpdb->prefix . 'fvm_cache';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
  $sqlb_table_name = $wpdb->prefix . 'fvm_logs';
358
+
359
+ # create cache table
360
+ $sqla = "CREATE TABLE {$sqla_table_name} (
361
+ id bigint(20) unsigned NOT NULL auto_increment ,
362
+ uid varchar(64) NOT NULL,
363
+ date bigint(10) unsigned NOT NULL,
364
+ type varchar(3) NOT NULL,
365
+ content mediumtext NOT NULL,
366
+ meta mediumtext NOT NULL,
367
+ PRIMARY KEY (id),
368
+ UNIQUE KEY uid (uid),
369
+ KEY date (date), KEY type (type)
370
+ ) $charset_collate;";
371
+
372
+ # create logs table
373
+ $sqlb = "CREATE TABLE {$sqlb_table_name} (
374
+ id bigint(20) unsigned NOT NULL auto_increment,
375
+ uid varchar(64) NOT NULL,
376
+ date bigint(10) unsigned NOT NULL,
377
+ type varchar(10) NOT NULL,
378
+ msg mediumtext NOT NULL,
379
+ meta mediumtext NOT NULL,
380
+ PRIMARY KEY (id),
381
+ UNIQUE KEY uid (uid),
382
+ KEY date (date),
383
+ KEY type (type)
384
+ ) $charset_collate;";
385
 
386
  # run sql
387
+ # https://developer.wordpress.org/reference/functions/dbdelta/
388
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
389
+ dbDelta( $sqla );
390
+ dbDelta( $sqlb );
391
+
392
+ # test if at least one table exists
393
+ if (!$wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $sqla_table_name)) === $sqla_table_name) {
394
+
395
+ # log
396
+ $err = 'An error occurred when trying to create the database tables';
397
+ error_log($err);
398
+
399
+ # alert
400
+ if(is_admin()) {
401
+ $notices = array($err);
402
+ set_transient('fvm_admin_notice_'.get_current_user_id(), json_encode($notices), 10);
403
+ }
404
+
405
+ # try again in 1 hour
406
+ set_transient('fvm_table_checker', true, HOUR_IN_SECONDS);
407
+
408
+ } else {
409
+ # success, but check again tomorrow
410
+ set_transient('fvm_table_checker', true, DAY_IN_SECONDS);
411
+ }
412
 
413
  }
414
 
416
  # run during deactivation
417
  register_deactivation_hook($fvm_var_file, 'fvm_plugin_deactivate');
418
  function fvm_plugin_deactivate() {
419
+
420
+ # process cache settings
421
+ fvm_purge_static_files();
422
 
423
  global $wpdb;
424
  if(is_null($wpdb)) { return false; }
426
  # remove options and tables
427
  $wpdb->query("DELETE FROM {$wpdb->prefix}options WHERE option_name = 'fvm_last_cache_update'");
428
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_cache");
429
+ $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
 
 
 
430
 
431
  }
432
 
433
  # run during uninstall
434
  register_uninstall_hook($fvm_var_file, 'fvm_plugin_uninstall');
435
  function fvm_plugin_uninstall() {
436
+
437
+ # process cache settings
438
+ fvm_purge_static_files();
439
+
440
  global $wpdb;
441
  if(is_null($wpdb)) { return false; }
442
 
446
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_cache");
447
  $wpdb->query("DROP TABLE IF EXISTS {$wpdb->prefix}fvm_logs");
448
 
 
 
 
449
  }
450
 
451
 
inc/common.php CHANGED
@@ -214,7 +214,7 @@ function fvm_purge_others(){
214
  do_action("swcfpc_purge_everything");
215
  return __( 'All caches on <strong>WP Cloudflare Super Page Cache</strong> have been purged.', 'fast-velocity-minify' );
216
  }
217
-
218
  # Purge Hyper Cache
219
  if (class_exists( 'HyperCache' )) {
220
  do_action( 'autoptimize_action_cachepurged' );
@@ -255,6 +255,30 @@ function fvm_purge_others(){
255
  do_action('wpo_cache_flush');
256
  return __( 'All caches on <strong>WP-Optimize</strong> have been purged.', 'fast-velocity-minify' );
257
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
  # hosting companies
260
 
@@ -316,15 +340,59 @@ function fvm_purge_others(){
316
  if(function_exists('pantheon_wp_clear_edge_all')) {
317
  pantheon_wp_clear_edge_all();
318
  }
 
 
 
 
 
319
 
320
- # wordpress default cache
321
- if (function_exists('wp_cache_flush')) {
322
- wp_cache_flush();
 
 
 
 
 
 
 
323
  }
324
 
325
  }
326
 
327
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  # Purge Godaddy Managed WordPress Hosting (Varnish)
329
  function fvm_godaddy_request( $method) {
330
  $url = home_url();
@@ -456,24 +524,41 @@ function fvm_generate_min_url($url, $tkey, $type, $code) {
456
  if(isset($ch_info['ch_url']) && !empty($ch_info['ch_url']) && isset($ch_info['ch_dir']) && !empty($ch_info['ch_dir'])) {
457
  if(is_dir($ch_info['ch_dir']) && is_writable($ch_info['ch_dir'])) {
458
 
459
- # filename
460
- $file = $ch_info['ch_dir'] . DIRECTORY_SEPARATOR . $filename;
461
- $public = $ch_info['ch_url'] . '/' .$filename;
462
-
463
- # wordpress functions
464
- require_once (ABSPATH . DIRECTORY_SEPARATOR . 'wp-admin'. DIRECTORY_SEPARATOR .'includes'. DIRECTORY_SEPARATOR .'class-wp-filesystem-base.php');
465
- require_once (ABSPATH . DIRECTORY_SEPARATOR .'wp-admin'. DIRECTORY_SEPARATOR .'includes'. DIRECTORY_SEPARATOR .'class-wp-filesystem-direct.php');
466
-
467
- # initialize
468
- $fileSystemDirect = new WP_Filesystem_Direct(false);
469
-
470
- # create if doesn't exist
471
- if(!$fileSystemDirect->exists($file) || ($fileSystemDirect->exists($file) && $fileSystemDirect->mtime($file) < $tvers)) {
472
- $fileSystemDirect->put_contents($file, $code);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  }
474
-
475
- # return url
476
- return $public;
477
 
478
  }
479
  }
@@ -549,34 +634,51 @@ function fvm_purge_static_files() {
549
  if(isset($ch_info['ch_url']) && !empty($ch_info['ch_url']) && isset($ch_info['ch_dir']) && !empty($ch_info['ch_dir'])) {
550
  if(is_dir($ch_info['ch_dir']) && is_writable($ch_info['ch_dir'])) {
551
 
552
- # wordpress functions
553
- require_once (ABSPATH . DIRECTORY_SEPARATOR . 'wp-admin'. DIRECTORY_SEPARATOR .'includes'. DIRECTORY_SEPARATOR .'class-wp-filesystem-base.php');
554
- require_once (ABSPATH . DIRECTORY_SEPARATOR .'wp-admin'. DIRECTORY_SEPARATOR .'includes'. DIRECTORY_SEPARATOR .'class-wp-filesystem-direct.php');
555
-
556
- # start
557
- $fileSystemDirect = new WP_Filesystem_Direct(false);
558
-
559
- # instant purge
560
- global $fvm_settings;
561
- if(isset($fvm_settings['cache']['min_instant_purge']) && $fvm_settings['cache']['min_instant_purge'] == true) {
562
- $fileSystemDirect->rmdir($ch_info['ch_dir'], true);
563
- return true;
564
- } else {
565
-
566
- # older than 24h and not matching current timestamp
567
- $list = $fileSystemDirect->dirlist($ch_info['ch_dir'], false, true);
568
- if(is_array($list) && count($list) > 0) {
569
- foreach($list as $k=>$arr) {
570
- if(isset($arr['lastmodunix']) && $arr['type'] == 'f' && intval($arr['lastmodunix']) <= time()-86400) {
571
- if(substr($arr['name'], 0, 10) !== time()) {
572
- $fileSystemDirect->delete($ch_info['ch_dir'] . DIRECTORY_SEPARATOR . $arr['name'], false, 'f');
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  }
574
  }
575
- }
576
- }
577
 
578
- }
579
 
 
 
 
 
 
 
580
  }
581
  }
582
 
@@ -950,20 +1052,13 @@ function fvm_replace_css_imports($css, $rq=null) {
950
 
951
 
952
  # remove fonts and icons from final css
953
- function fvm_extract_fonts($css_code) {
954
 
955
  global $fvm_settings, $fvm_urls;
956
- $critical_fonts = array();
957
  $mff = array();
958
  $css_preload = array();
959
  $css_code_ff = '';
960
 
961
- # get list of fonts that are on the critical path and are to be left alone
962
- if( isset($fvm_settings['css']['css_optimize_critical_fonts']) &&
963
- !empty($fvm_settings['css']['css_optimize_critical_fonts'])) {
964
- $critical_fonts = fvm_string_toarray($fvm_settings['css']['css_optimize_critical_fonts']);
965
- }
966
-
967
  # extract font faces
968
  preg_match_all('/\@\s*font-face\s*\{([^}]+)\}/iUu', $css_code, $mff);
969
  if(isset($mff[0]) && is_array($mff[0])) {
@@ -976,6 +1071,50 @@ function fvm_extract_fonts($css_code) {
976
  }
977
  }
978
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
979
  # relative paths
980
  $css_code_ff = str_replace('https://'.$fvm_urls['wp_domain'], '', $css_code_ff);
981
  $css_code_ff = str_replace('http://'.$fvm_urls['wp_domain'], '', $css_code_ff);
@@ -999,7 +1138,7 @@ function fvm_process_cdn($html) {
999
  # html must be an object
1000
  if (!is_object($html)) {
1001
  $nobj = 1;
1002
- $html = str_get_html($html, true, true, 'UTF-8', false, PHP_EOL, ' ');
1003
  }
1004
 
1005
  # default integration
@@ -1653,32 +1792,9 @@ function fvm_can_process_common() {
1653
  return true;
1654
  }
1655
 
1656
- # check if the user is logged in, and if the user role allows optimization
1657
- function fvm_user_role_processing_allowed($group) {
1658
- if(function_exists('is_user_logged_in') && function_exists('wp_get_current_user')) {
1659
- if(is_user_logged_in()) {
1660
-
1661
- # get user roles
1662
- global $fvm_settings;
1663
- $user = wp_get_current_user();
1664
- $roles = (array) $user->roles;
1665
- foreach($roles as $role) {
1666
- if(isset($fvm_settings['minify'][$role]) && $fvm_settings['minify'][$role] == true) {
1667
- return true;
1668
- }
1669
- }
1670
-
1671
- # disable for other logged in users by default
1672
- return false;
1673
- }
1674
- }
1675
-
1676
- # allow by default
1677
- return true;
1678
- }
1679
 
1680
  # check if we can process the page, minimum filters
1681
- function fvm_can_process_query_string($loc) {
1682
 
1683
  # host and uri path
1684
  $host = fvm_get_domain();
@@ -1693,11 +1809,17 @@ function fvm_can_process_query_string($loc) {
1693
  # check
1694
  $qsarr = array(); parse_str($parse["query"], $qsarr);
1695
 
1696
- # allowed queries by default
1697
- if(isset($qsarr['s'])) { unset($qsarr['s']); } # search
1698
- if(isset($qsarr['lang'])) { unset($qsarr['lang']); } # wpml
1699
-
1700
- # if there are other queries left, bypass cache
 
 
 
 
 
 
1701
  if(count($qsarr) > 0) {
1702
  return false;
1703
  }
@@ -1707,6 +1829,29 @@ function fvm_can_process_query_string($loc) {
1707
  return true;
1708
  }
1709
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1710
 
1711
 
1712
  # check if page is amp
214
  do_action("swcfpc_purge_everything");
215
  return __( 'All caches on <strong>WP Cloudflare Super Page Cache</strong> have been purged.', 'fast-velocity-minify' );
216
  }
217
+
218
  # Purge Hyper Cache
219
  if (class_exists( 'HyperCache' )) {
220
  do_action( 'autoptimize_action_cachepurged' );
255
  do_action('wpo_cache_flush');
256
  return __( 'All caches on <strong>WP-Optimize</strong> have been purged.', 'fast-velocity-minify' );
257
  }
258
+
259
+ # nginx helper
260
+ if(has_action('rt_nginx_helper_purge_all')) {
261
+ do_action('rt_nginx_helper_purge_all');
262
+ $ret[] = __( 'Nginx Helper' );
263
+ }
264
+
265
+
266
+ # Object Cache
267
+
268
+ # WordPress OPCache
269
+ if (function_exists('wp_cache_flush')) {
270
+ wp_cache_flush();
271
+ }
272
+
273
+ # Purge Redis Object Cache plugin
274
+ if(class_exists('Redis') && defined('WP_REDIS_PATH')) {
275
+ $r = new Redis();
276
+ if ($r->connect( WP_REDIS_PATH, 0 )) {
277
+ if( false !== $r->flushdb() ) {
278
+ $ret[] = __('Redis Object Cache');
279
+ }
280
+ }
281
+ }
282
 
283
  # hosting companies
284
 
340
  if(function_exists('pantheon_wp_clear_edge_all')) {
341
  pantheon_wp_clear_edge_all();
342
  }
343
+
344
+ # cloudways varnish
345
+ if(fvm_purge_varnish_cloudways()) {
346
+ $ret[] = __('Cloudways (Varnish)');
347
+ }
348
 
349
+ # bigscoots.com
350
+ if(has_action('bs_cache_purge_cache')) {
351
+ do_action('bs_cache_purge_cache');
352
+ $ret[] = __( 'BigScoots' );
353
+ }
354
+
355
+ # godaddy.com managed WordPress
356
+ if (class_exists('WPass') && method_exists('WPass\Cache', 'do_ban')){
357
+ WPaaS\Cache::do_ban();
358
+ $ret[] = __( 'GoDaddy' );
359
  }
360
 
361
  }
362
 
363
 
364
+ # purge varnish on cloudways
365
+ function fvm_purge_varnish_cloudways() {
366
+
367
+ # cloudways detection
368
+ if (!isset($_SERVER['cw_allowed_ip'])){ return false; }
369
+
370
+ # must have
371
+ if (!isset($_SERVER['HTTP_X_VARNISH']) || !isset($_SERVER['HTTP_X_APPLICATION'])){ return false; }
372
+ if (is_null($_SERVER['HTTP_X_VARNISH']) || is_null($_SERVER['HTTP_X_APPLICATION'])){ return false; }
373
+ if ('varnishpass' === trim($_SERVER['HTTP_X_APPLICATION'])){ return false; }
374
+ if ('bypass' === trim($_SERVER['HTTP_X_APPLICATION'])){ return false; }
375
+
376
+ # host and uri path
377
+ $host = wpraiser_get_domain();
378
+
379
+ # request arguments
380
+ $request_args = array('method' => 'PURGE', 'redirection' => 0, 'timeout' => 10, 'blocking' => false, 'headers' => array('Host' => $host, 'X-Purge-Method' => 'regex') );
381
+
382
+ # default host and port
383
+ $varnish_ip = '127.0.0.1';
384
+ $varnish_port = '8080';
385
+
386
+ # overwrite by constant
387
+ if(defined('FVM_VARNISH_IP') && !empty(FVM_VARNISH_IP)) { $varnish_ip = trim(FVM_VARNISH_IP); }
388
+ if(defined('FVM_VARNISH_PORT') && !empty(FVM_VARNISH_PORT)) { $varnish_port = trim(FVM_VARNISH_PORT); }
389
+
390
+ # purge async
391
+ $response = wp_remote_request('http://'.$varnish_ip.':'.$varnish_port.'/.*', $request_args);
392
+ return true;
393
+ }
394
+
395
+
396
  # Purge Godaddy Managed WordPress Hosting (Varnish)
397
  function fvm_godaddy_request( $method) {
398
  $url = home_url();
524
  if(isset($ch_info['ch_url']) && !empty($ch_info['ch_url']) && isset($ch_info['ch_dir']) && !empty($ch_info['ch_dir'])) {
525
  if(is_dir($ch_info['ch_dir']) && is_writable($ch_info['ch_dir'])) {
526
 
527
+ # get file system type
528
+ if(function_exists('get_filesystem_method')) {
529
+ $access_type = get_filesystem_method();
530
+ if($access_type === 'direct') {
531
+
532
+ # get credentials
533
+ $creds = request_filesystem_credentials(site_url() . '/wp-admin/', '', false, false, array());
534
+
535
+ /* initialize the API */
536
+ if ( ! WP_Filesystem($creds) ) {
537
+ error_log('FVM could not find the WP filesystem.');
538
+ return false;
539
+ }
540
+
541
+ global $wp_filesystem;
542
+ /* do our file manipulations below */
543
+
544
+ # filename
545
+ $file = $ch_info['ch_dir'] . DIRECTORY_SEPARATOR . $filename;
546
+ $public = $ch_info['ch_url'] . '/' .$filename;
547
+
548
+ # create if doesn't exist
549
+ if(!$wp_filesystem->exists($file) || ($wp_filesystem->exists($file) && $wp_filesystem->mtime($file) < $tvers)) {
550
+ $wp_filesystem->put_contents($file, $code);
551
+ }
552
+
553
+ # return url
554
+ return $public;
555
+
556
+
557
+ } else {
558
+ /* don't have direct write access. Prompt user with our notice */
559
+ error_log('FVM has no direct write access for CSS / JS cache files.');
560
+ }
561
  }
 
 
 
562
 
563
  }
564
  }
634
  if(isset($ch_info['ch_url']) && !empty($ch_info['ch_url']) && isset($ch_info['ch_dir']) && !empty($ch_info['ch_dir'])) {
635
  if(is_dir($ch_info['ch_dir']) && is_writable($ch_info['ch_dir'])) {
636
 
637
+ # get file system type
638
+ if(function_exists('get_filesystem_method')) {
639
+ $access_type = get_filesystem_method();
640
+ if($access_type === 'direct') {
641
+
642
+ # get credentials
643
+ $creds = request_filesystem_credentials(site_url() . '/wp-admin/', '', false, false, array());
644
+
645
+ /* initialize the API */
646
+ if ( ! WP_Filesystem($creds) ) {
647
+ error_log('FVM could not find the WP filesystem.');
648
+ return false;
649
+ }
650
+
651
+ global $wp_filesystem;
652
+ /* do our file manipulations below */
653
+
654
+ # instant purge
655
+ if(isset($fvm_settings['cache']['min_instant_purge']) && $fvm_settings['cache']['min_instant_purge'] == true) {
656
+ $wp_filesystem->rmdir($ch_info['ch_dir'], true);
657
+ error_log('FVM instant cache purge.');
658
+ return true;
659
+ } else {
660
+
661
+ # older than 7 days and not matching current timestamp
662
+ error_log('FVM cache purge for files older than 7 days ago.');
663
+ $list = $wp_filesystem->dirlist($ch_info['ch_dir'], false, true);
664
+ if(is_array($list) && count($list) > 0) {
665
+ foreach($list as $k=>$arr) {
666
+ if(isset($arr['lastmodunix']) && $arr['type'] == 'f' && intval($arr['lastmodunix']) <= time()-86400*7) {
667
+ if(substr($arr['name'], 0, 10) !== time()) {
668
+ $wp_filesystem->delete($ch_info['ch_dir'] . DIRECTORY_SEPARATOR . $arr['name'], false, 'f');
669
+ }
670
+ }
671
  }
672
  }
 
 
673
 
674
+ }
675
 
676
+ } else {
677
+ /* don't have direct write access. Prompt user with our notice */
678
+ error_log('FVM has no direct write access for CSS / JS cache files.');
679
+ }
680
+ }
681
+
682
  }
683
  }
684
 
1052
 
1053
 
1054
  # remove fonts and icons from final css
1055
+ function fvm_extract_fonts($css_code, $url=null) {
1056
 
1057
  global $fvm_settings, $fvm_urls;
 
1058
  $mff = array();
1059
  $css_preload = array();
1060
  $css_code_ff = '';
1061
 
 
 
 
 
 
 
1062
  # extract font faces
1063
  preg_match_all('/\@\s*font-face\s*\{([^}]+)\}/iUu', $css_code, $mff);
1064
  if(isset($mff[0]) && is_array($mff[0])) {
1071
  }
1072
  }
1073
 
1074
+ # add font-display swap for all font faces
1075
+ # https://developers.google.com/web/updates/2016/02/font-display
1076
+ $css_code_ff = preg_replace_callback('/(?:@font-face)\s*{(?<value>[^}]+)}/i',
1077
+ function ($matches) {
1078
+ if(stripos($matches['value'], 'font-display') !== false) {
1079
+ return $matches[0];
1080
+ } else {
1081
+ return str_replace($matches['value'], 'font-display:swap;'.$matches['value'], $matches[0] );
1082
+ }
1083
+ },
1084
+ $css_code_ff
1085
+ );
1086
+
1087
+ # add font-display swap for all font faces
1088
+ # https://developers.google.com/web/updates/2016/02/font-display
1089
+ $css_code = preg_replace_callback('/(?:@font-face)\s*{(?<value>[^}]+)}/i',
1090
+ function ($matches) {
1091
+ if(stripos($matches['value'], 'font-display') !== false) {
1092
+ return $matches[0];
1093
+ } else {
1094
+ return str_replace($matches['value'], 'font-display:swap;'.$matches['value'], $matches[0] );
1095
+ }
1096
+ },
1097
+ $css_code
1098
+ );
1099
+
1100
+ # remove query strings from fonts
1101
+ $css_code_ff = preg_replace('/(.eot|.woff2|.woff|.ttf)+[?+](.+?)(\)|\'|\")/ui', "$1"."$3", $css_code_ff);
1102
+ $css_code = preg_replace('/(.eot|.woff2|.woff|.ttf)+[?+](.+?)(\)|\'|\")/ui', "$1"."$3", $css_code);
1103
+
1104
+ # fix url paths css_code_ff
1105
+ if(!empty($url)) {
1106
+ $matches = array(); preg_match_all("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", $css_code_ff, $matches);
1107
+ foreach($matches[1] as $a) { $b = trim($a); if($b != $a) { $css_code_ff = str_replace($a, $b, $css_code_ff); } }
1108
+ $css_code_ff = preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"#])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css_code_ff);
1109
+ }
1110
+
1111
+ # fix url paths css_code
1112
+ if(!empty($url)) {
1113
+ $matches = array(); preg_match_all("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"])(.+?)['\"]?\s*\)/ui", $css_code, $matches);
1114
+ foreach($matches[1] as $a) { $b = trim($a); if($b != $a) { $css_code = str_replace($a, $b, $css_code); } }
1115
+ $css_code = preg_replace("/url\(\s*['\"]?(?!data:)(?!http)(?![\/'\"#])(.+?)['\"]?\s*\)/ui", "url(".dirname($url)."/$1)", $css_code);
1116
+ }
1117
+
1118
  # relative paths
1119
  $css_code_ff = str_replace('https://'.$fvm_urls['wp_domain'], '', $css_code_ff);
1120
  $css_code_ff = str_replace('http://'.$fvm_urls['wp_domain'], '', $css_code_ff);
1138
  # html must be an object
1139
  if (!is_object($html)) {
1140
  $nobj = 1;
1141
+ $html = fvm_str_get_html($html, true, true, 'UTF-8', false, PHP_EOL, ' ');
1142
  }
1143
 
1144
  # default integration
1792
  return true;
1793
  }
1794
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1795
 
1796
  # check if we can process the page, minimum filters
1797
+ function fvm_can_process_query_string() {
1798
 
1799
  # host and uri path
1800
  $host = fvm_get_domain();
1809
  # check
1810
  $qsarr = array(); parse_str($parse["query"], $qsarr);
1811
 
1812
+ # remove allowed query strings from the list of detected queries
1813
+ if(isset($fvm_settings['settings']['qs']) && !empty($fvm_settings['settings']['qs'])) {
1814
+ $arr = fvm_string_toarray($fvm_settings['settings']['qs']);
1815
+ if(is_array($arr) && count($arr) > 0) {
1816
+ foreach ($arr as $a) {
1817
+ if(isset($qsarr[$e])) { unset($qsarr[$e]); }
1818
+ }
1819
+ }
1820
+ }
1821
+
1822
+ # if there are other queries left, bypass processing
1823
  if(count($qsarr) > 0) {
1824
  return false;
1825
  }
1829
  return true;
1830
  }
1831
 
1832
+ # check if the user is logged in, and if the user role allows optimization
1833
+ function fvm_user_role_processing_allowed($group) {
1834
+ if(function_exists('is_user_logged_in') && function_exists('wp_get_current_user')) {
1835
+ if(is_user_logged_in()) {
1836
+
1837
+ # get user roles
1838
+ global $fvm_settings;
1839
+ $user = wp_get_current_user();
1840
+ $roles = (array) $user->roles;
1841
+ foreach($roles as $role) {
1842
+ if(isset($fvm_settings['minify'][$role]) && $fvm_settings['minify'][$role] == true) {
1843
+ return true;
1844
+ }
1845
+ }
1846
+
1847
+ # disable for other logged in users by default
1848
+ return false;
1849
+ }
1850
+ }
1851
+
1852
+ # allow by default
1853
+ return true;
1854
+ }
1855
 
1856
 
1857
  # check if page is amp
inc/frontend.php CHANGED
@@ -47,7 +47,7 @@ function fvm_process_page($html) {
47
 
48
  # get html into an object
49
  # https://simplehtmldom.sourceforge.io/manual.htm
50
- $html_object = str_get_html($html, false, true, 'UTF-8', false, PHP_EOL, ' ');
51
 
52
  # return early if html is not an object, or overwrite html into an object for processing
53
  if (!is_object($html_object)) {
@@ -188,7 +188,7 @@ function fvm_process_page($html) {
188
 
189
  # extract fonts and icons
190
  if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
191
- $extract_fonts_arr = fvm_extract_fonts($css['code']);
192
  $css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
193
  $css_code = $extract_fonts_arr['code'];
194
  } else {
@@ -259,7 +259,7 @@ function fvm_process_page($html) {
259
 
260
  # extract fonts and icons
261
  if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
262
- $extract_fonts_arr = fvm_extract_fonts($css['code']);
263
  $css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
264
  $css_code = $extract_fonts_arr['code'];
265
  } else {
47
 
48
  # get html into an object
49
  # https://simplehtmldom.sourceforge.io/manual.htm
50
+ $html_object = fvm_str_get_html($html, false, true, 'UTF-8', false, PHP_EOL, ' ');
51
 
52
  # return early if html is not an object, or overwrite html into an object for processing
53
  if (!is_object($html_object)) {
188
 
189
  # extract fonts and icons
190
  if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
191
+ $extract_fonts_arr = fvm_extract_fonts($css['code'], $href);
192
  $css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
193
  $css_code = $extract_fonts_arr['code'];
194
  } else {
259
 
260
  # extract fonts and icons
261
  if(isset($fvm_settings['css']['fonts']) && $fvm_settings['css']['fonts'] == true) {
262
+ $extract_fonts_arr = fvm_extract_fonts($css['code'], $href);
263
  $css_lowpriority_code.= '/* '.$href.' */'. PHP_EOL . $extract_fonts_arr['fonts'];
264
  $css_code = $extract_fonts_arr['code'];
265
  } else {
layout/admin-layout-help.php CHANGED
@@ -285,6 +285,21 @@
285
  </div>
286
  </div>
287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  <div style="height: 20px;"></div>
289
  <h2 class="title">User Settings</h2>
290
 
285
  </div>
286
  </div>
287
 
288
+ <div style="height: 20px;"></div>
289
+ <h2 class="title">Query String Settings</h2>
290
+
291
+ <div class="accordion">
292
+ <h3>Allowed Query Strings</h3>
293
+ <div>
294
+ <p><strong>Notes:</strong></p>
295
+ <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>
296
+ <p><strong>Example Settings:</strong></p>
297
+ <p class="fvm-code-full">
298
+ 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
299
+ </p>
300
+ </div>
301
+ </div>
302
+
303
  <div style="height: 20px;"></div>
304
  <h2 class="title">User Settings</h2>
305
 
layout/admin-layout-settings.php CHANGED
@@ -22,7 +22,7 @@
22
  <fieldset>
23
  <label for="fvm_settings_cache_min_instant_purge">
24
  <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')); ?>>
25
- <?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>
26
  <br />
27
 
28
  </fieldset></td>
@@ -160,7 +160,7 @@
160
  <td><fieldset>
161
  <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>
162
  <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>
163
- <p class="description">[ <?php _e( 'This will allow you to remove unwanted CSS files by URL path from the frontend', 'fast-velocity-minify' ); ?> ]</p>
164
  <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>
165
  </fieldset></td>
166
  </tr>
@@ -170,7 +170,7 @@
170
  <td><fieldset>
171
  <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>
172
  <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>
173
- <p class="description">[ <?php _e( 'This will allow you to remove unwanted CSS files by URL path from the frontend', 'fast-velocity-minify' ); ?> ]</p>
174
  <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>
175
  </fieldset></td>
176
  </tr>
@@ -363,6 +363,22 @@ www.googletagmanager.com/gtm.js"><?php echo fvm_get_settings_value($fvm_settings
363
  </tbody></table>
364
 
365
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  <div style="height: 60px;"></div>
367
  <h2 class="title"><?php _e( 'User Settings', 'fast-velocity-minify' ); ?></h2>
368
  <h3 class="fvm-bold-green"><?php _e( 'For compatibility reasons, only anonymous users should be optimized by default.', 'fast-velocity-minify' ); ?></h3>
22
  <fieldset>
23
  <label for="fvm_settings_cache_min_instant_purge">
24
  <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')); ?>>
25
+ <?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>
26
  <br />
27
 
28
  </fieldset></td>
160
  <td><fieldset>
161
  <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>
162
  <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>
163
+ <p class="description">[ <?php _e( 'This will allow you to remove unwanted CSS files by URI path from the frontend', 'fast-velocity-minify' ); ?> ]</p>
164
  <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>
165
  </fieldset></td>
166
  </tr>
170
  <td><fieldset>
171
  <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>
172
  <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>
173
+ <p class="description">[ <?php _e( 'This will allow you to Async CSS files by URI path from the frontend', 'fast-velocity-minify' ); ?> ]</p>
174
  <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>
175
  </fieldset></td>
176
  </tr>
363
  </tbody></table>
364
 
365
 
366
+ <div style="height: 60px;"></div>
367
+ <h2 class="title"><?php _e( 'Query Strings', 'fast-velocity-minify' ); ?></h2>
368
+ <h3 class="fvm-bold-green"><?php _e( 'Allow processing of CSS, JS & HTML on specific query strings', 'fast-velocity-minify' ); ?></h3>
369
+ <table class="form-table fvm-settings">
370
+ <tbody>
371
+ <tr>
372
+ <th scope="row"><?php _e( 'Allowed Query Strings', 'fast-velocity-minify' ); ?></th>
373
+ <td><fieldset>
374
+ <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>
375
+ <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>
376
+ <p class="description">[ <?php _e( 'Additional query strings, keys only', 'fast-velocity-minify' ); ?> ]</p>
377
+ </fieldset></td>
378
+ </tr>
379
+ </tbody></table>
380
+
381
+
382
  <div style="height: 60px;"></div>
383
  <h2 class="title"><?php _e( 'User Settings', 'fast-velocity-minify' ); ?></h2>
384
  <h3 class="fvm-bold-green"><?php _e( 'For compatibility reasons, only anonymous users should be optimized by default.', 'fast-velocity-minify' ); ?></h3>
libs/matthiasmullie/minify/src/CSS.php CHANGED
@@ -563,47 +563,7 @@ class CSS extends Minify
563
  */
564
  protected function shortenZeroes($content)
565
  {
566
- // we don't want to strip units in `calc()` expressions:
567
- // `5px - 0px` is valid, but `5px - 0` is not
568
- // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
569
- // `10 * 0` is invalid
570
- // we've extracted calcs earlier, so we don't need to worry about this
571
-
572
- // reusable bits of code throughout these regexes:
573
- // before & after are used to make sure we don't match lose unintended
574
- // 0-like values (e.g. in #000, or in http://url/1.0)
575
- // units can be stripped from 0 values, or used to recognize non 0
576
- // values (where wa may be able to strip a .0 suffix)
577
- $before = '(?<=[:(, ])';
578
- $after = '(?=[ ,);}])';
579
- $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)';
580
-
581
- // strip units after zeroes (0px -> 0)
582
- // NOTE: it should be safe to remove all units for a 0 value, but in
583
- // practice, Webkit (especially Safari) seems to stumble over at least
584
- // 0%, potentially other units as well. Only stripping 'px' for now.
585
- // @see https://github.com/matthiasmullie/minify/issues/60
586
- $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
587
-
588
- // strip 0-digits (.0 -> 0)
589
- $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
590
- // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
591
- $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
592
- // strip trailing 0: 50.00 -> 50, 50.00px -> 50px
593
- $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
594
- // strip leading 0: 0.1 -> .1, 01.1 -> 1.1
595
- $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
596
-
597
- // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
598
- $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
599
-
600
- // IE doesn't seem to understand a unitless flex-basis value (correct -
601
- // it goes against the spec), so let's add it in again (make it `%`,
602
- // which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
603
- // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
604
- $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
605
- $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
606
-
607
  return $content;
608
  }
609
 
563
  */
564
  protected function shortenZeroes($content)
565
  {
566
+ // removed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  return $content;
568
  }
569
 
libs/simplehtmldom/simple_html_dom.php CHANGED
@@ -26,32 +26,32 @@ if (!defined('ABSPATH')){ exit(); }
26
 
27
  # mod
28
  defined('FVM_MAX_FILE_SIZE') || define('FVM_MAX_FILE_SIZE', 2000000); # Process HTML up to 2 Mb
29
- defined('DEFAULT_TARGET_CHARSET') || define('DEFAULT_TARGET_CHARSET', 'UTF-8');
30
- defined('DEFAULT_BR_TEXT') || define('DEFAULT_BR_TEXT', "\r\n");
31
- defined('DEFAULT_SPAN_TEXT') || define('DEFAULT_SPAN_TEXT', ' ');
32
 
33
  # other
34
- define('HDOM_TYPE_ELEMENT', 1);
35
- define('HDOM_TYPE_COMMENT', 2);
36
- define('HDOM_TYPE_TEXT', 3);
37
- define('HDOM_TYPE_ENDTAG', 4);
38
- define('HDOM_TYPE_ROOT', 5);
39
- define('HDOM_TYPE_UNKNOWN', 6);
40
- define('HDOM_QUOTE_DOUBLE', 0);
41
- define('HDOM_QUOTE_SINGLE', 1);
42
- define('HDOM_QUOTE_NO', 3);
43
- define('HDOM_INFO_BEGIN', 0);
44
- define('HDOM_INFO_END', 1);
45
- define('HDOM_INFO_QUOTE', 2);
46
- define('HDOM_INFO_SPACE', 3);
47
- define('HDOM_INFO_TEXT', 4);
48
- define('HDOM_INFO_INNER', 5);
49
- define('HDOM_INFO_OUTER', 6);
50
- define('HDOM_INFO_ENDSPACE', 7);
51
- define('HDOM_SMARTY_AS_TEXT', 1);
52
 
53
  # functions
54
- function file_get_html(
55
  $url,
56
  $use_include_path = false,
57
  $context = null,
@@ -59,10 +59,10 @@ function file_get_html(
59
  $maxLen = -1,
60
  $lowercase = true,
61
  $forceTagsClosed = true,
62
- $target_charset = DEFAULT_TARGET_CHARSET,
63
  $stripRN = true,
64
- $defaultBRText = DEFAULT_BR_TEXT,
65
- $defaultSpanText = DEFAULT_SPAN_TEXT)
66
  {
67
  if($maxLen <= 0) { $maxLen = FVM_MAX_FILE_SIZE; }
68
 
@@ -97,14 +97,14 @@ function file_get_html(
97
  return $dom->load($contents, $lowercase, $stripRN);
98
  }
99
 
100
- function str_get_html(
101
  $str,
102
  $lowercase = true,
103
  $forceTagsClosed = true,
104
- $target_charset = DEFAULT_TARGET_CHARSET,
105
  $stripRN = true,
106
- $defaultBRText = DEFAULT_BR_TEXT,
107
- $defaultSpanText = DEFAULT_SPAN_TEXT)
108
  {
109
  $dom = new simple_html_dom(
110
  null,
@@ -124,14 +124,14 @@ function str_get_html(
124
  return $dom->load($str, $lowercase, $stripRN);
125
  }
126
 
127
- function dump_html_tree($node, $show_attr = true, $deep = 0)
128
  {
129
  $node->dump($node);
130
  }
131
 
132
- class simple_html_dom_node
133
  {
134
- public $nodetype = HDOM_TYPE_TEXT;
135
  public $tag = 'text';
136
  public $attr = array();
137
  public $children = array();
@@ -218,10 +218,10 @@ class simple_html_dom_node
218
  $string .= " text: ({$this->text})";
219
  }
220
 
221
- $string .= ' HDOM_INNER_INFO: ';
222
 
223
- if (isset($node->_[HDOM_INFO_INNER])) {
224
- $string .= "'" . $node->_[HDOM_INFO_INNER] . "'";
225
  } else {
226
  $string .= ' NULL ';
227
  }
@@ -345,12 +345,12 @@ class simple_html_dom_node
345
 
346
  function innertext()
347
  {
348
- if (isset($this->_[HDOM_INFO_INNER])) {
349
- return $this->_[HDOM_INFO_INNER];
350
  }
351
 
352
- if (isset($this->_[HDOM_INFO_TEXT])) {
353
- return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
354
  }
355
 
356
  $ret = '';
@@ -387,24 +387,24 @@ class simple_html_dom_node
387
  call_user_func_array($this->dom->callback, array($this));
388
  }
389
 
390
- if (isset($this->_[HDOM_INFO_OUTER])) {
391
- return $this->_[HDOM_INFO_OUTER];
392
  }
393
 
394
- if (isset($this->_[HDOM_INFO_TEXT])) {
395
- return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
396
  }
397
 
398
  $ret = '';
399
 
400
- if ($this->dom && $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]) {
401
- $ret = $this->dom->nodes[$this->_[HDOM_INFO_BEGIN]]->makeup();
402
  }
403
 
404
- if (isset($this->_[HDOM_INFO_INNER])) {
405
- // todo: <br> should either never have HDOM_INFO_INNER or always
406
  if ($this->tag !== 'br') {
407
- $ret .= $this->_[HDOM_INFO_INNER];
408
  }
409
  } elseif ($this->nodes) {
410
  foreach ($this->nodes as $n) {
@@ -412,7 +412,7 @@ class simple_html_dom_node
412
  }
413
  }
414
 
415
- if (isset($this->_[HDOM_INFO_END]) && $this->_[HDOM_INFO_END] != 0) {
416
  $ret .= '</' . $this->tag . '>';
417
  }
418
 
@@ -421,14 +421,14 @@ class simple_html_dom_node
421
 
422
  function text()
423
  {
424
- if (isset($this->_[HDOM_INFO_INNER])) {
425
- return $this->_[HDOM_INFO_INNER];
426
  }
427
 
428
  switch ($this->nodetype) {
429
- case HDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
430
- case HDOM_TYPE_COMMENT: return '';
431
- case HDOM_TYPE_UNKNOWN: return '';
432
  }
433
 
434
  if (strcasecmp($this->tag, 'script') === 0) { return ''; }
@@ -436,7 +436,7 @@ class simple_html_dom_node
436
 
437
  $ret = '';
438
 
439
- // In rare cases, (always node type 1 or HDOM_TYPE_ELEMENT - observed
440
  // for some span tags, and some p tags) $this->nodes is set to NULL.
441
  // NOTE: This indicates that there is a problem where it's set to NULL
442
  // without a clear happening.
@@ -472,8 +472,8 @@ class simple_html_dom_node
472
  function makeup()
473
  {
474
  // text, comment, unknown
475
- if (isset($this->_[HDOM_INFO_TEXT])) {
476
- return $this->dom->restore_noise($this->_[HDOM_INFO_TEXT]);
477
  }
478
 
479
  $ret = '<' . $this->tag;
@@ -485,23 +485,23 @@ class simple_html_dom_node
485
  // skip removed attribute
486
  if ($val === null || $val === false) { continue; }
487
 
488
- $ret .= $this->_[HDOM_INFO_SPACE][$i][0];
489
 
490
  //no value attr: nowrap, checked selected...
491
  if ($val === true) {
492
  $ret .= $key;
493
  } else {
494
- switch ($this->_[HDOM_INFO_QUOTE][$i])
495
  {
496
- case HDOM_QUOTE_DOUBLE: $quote = '"'; break;
497
- case HDOM_QUOTE_SINGLE: $quote = '\''; break;
498
  default: $quote = '';
499
  }
500
 
501
  $ret .= $key
502
- . $this->_[HDOM_INFO_SPACE][$i][1]
503
  . '='
504
- . $this->_[HDOM_INFO_SPACE][$i][2]
505
  . $quote
506
  . $val
507
  . $quote;
@@ -509,7 +509,7 @@ class simple_html_dom_node
509
  }
510
 
511
  $ret = $this->dom->restore_noise($ret);
512
- return $ret . $this->_[HDOM_INFO_ENDSPACE] . '>';
513
  }
514
 
515
  function find($selector, $idx = null, $lowercase = false)
@@ -524,9 +524,9 @@ class simple_html_dom_node
524
  // code tracker id 2788009
525
  // used to be: if (($levle=count($selectors[0]))===0) return array();
526
  if (($levle = count($selectors[$c])) === 0) { return array(); }
527
- if (!isset($this->_[HDOM_INFO_BEGIN])) { return array(); }
528
 
529
- $head = array($this->_[HDOM_INFO_BEGIN] => 1);
530
  $cmd = ' '; // Combinator
531
 
532
  // handle descendant selectors, no recursive!
@@ -575,18 +575,18 @@ class simple_html_dom_node
575
  if ($parent_cmd === ' ') { // Descendant Combinator
576
  // Find parent closing tag if the current element doesn't have a closing
577
  // tag (i.e. void element)
578
- $end = (!empty($this->_[HDOM_INFO_END])) ? $this->_[HDOM_INFO_END] : 0;
579
  if ($end == 0) {
580
  $parent = $this->parent;
581
- while (!isset($parent->_[HDOM_INFO_END]) && $parent !== null) {
582
  $end -= 1;
583
  $parent = $parent->parent;
584
  }
585
- $end += $parent->_[HDOM_INFO_END];
586
  }
587
 
588
  // Get list of target nodes
589
- $nodes_start = $this->_[HDOM_INFO_BEGIN] + 1;
590
  $nodes_count = $end - $nodes_start;
591
  $nodes = array_slice($this->dom->nodes, $nodes_start, $nodes_count, true);
592
  } elseif ($parent_cmd === '>') { // Child Combinator
@@ -777,7 +777,7 @@ class simple_html_dom_node
777
  }
778
 
779
  // Found a match. Add to list and clear node
780
- if ($pass) $ret[$node->_[HDOM_INFO_BEGIN]] = 1;
781
  unset($node);
782
  }
783
  // It's passed by reference so this is actually what this function returns.
@@ -990,17 +990,17 @@ class simple_html_dom_node
990
  if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
991
 
992
  switch ($name) {
993
- case 'outertext': return $this->_[HDOM_INFO_OUTER] = $value;
994
  case 'innertext':
995
- if (isset($this->_[HDOM_INFO_TEXT])) {
996
- return $this->_[HDOM_INFO_TEXT] = $value;
997
  }
998
- return $this->_[HDOM_INFO_INNER] = $value;
999
  }
1000
 
1001
  if (!isset($this->attr[$name])) {
1002
- $this->_[HDOM_INFO_SPACE][] = array(' ', '', '');
1003
- $this->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_DOUBLE;
1004
  }
1005
 
1006
  $this->attr[$name] = $value;
@@ -1467,10 +1467,10 @@ class simple_html_dom
1467
  $str = null,
1468
  $lowercase = true,
1469
  $forceTagsClosed = true,
1470
- $target_charset = DEFAULT_TARGET_CHARSET,
1471
  $stripRN = true,
1472
- $defaultBRText = DEFAULT_BR_TEXT,
1473
- $defaultSpanText = DEFAULT_SPAN_TEXT,
1474
  $options = 0)
1475
  {
1476
  if ($str) {
@@ -1505,8 +1505,8 @@ class simple_html_dom
1505
  $str,
1506
  $lowercase = true,
1507
  $stripRN = true,
1508
- $defaultBRText = DEFAULT_BR_TEXT,
1509
- $defaultSpanText = DEFAULT_SPAN_TEXT,
1510
  $options = 0)
1511
  {
1512
  global $debug_object;
@@ -1541,14 +1541,14 @@ class simple_html_dom
1541
  // strip out server side scripts
1542
  $this->remove_noise("'(<\?)(.*?)(\?>)'s", true);
1543
 
1544
- if($options & HDOM_SMARTY_AS_TEXT) { // Strip Smarty scripts
1545
  $this->remove_noise("'(\{\w)(.*?)(\})'s", true);
1546
  }
1547
 
1548
  // parsing
1549
  $this->parse();
1550
  // end
1551
- $this->root->_[HDOM_INFO_END] = $this->cursor;
1552
  $this->parse_charset();
1553
 
1554
  // make load function chainable
@@ -1628,8 +1628,8 @@ class simple_html_dom
1628
 
1629
  protected function prepare(
1630
  $str, $lowercase = true,
1631
- $defaultBRText = DEFAULT_BR_TEXT,
1632
- $defaultSpanText = DEFAULT_SPAN_TEXT)
1633
  {
1634
  $this->clear();
1635
 
@@ -1643,10 +1643,10 @@ class simple_html_dom
1643
  $this->lowercase = $lowercase;
1644
  $this->default_br_text = $defaultBRText;
1645
  $this->default_span_text = $defaultSpanText;
1646
- $this->root = new simple_html_dom_node($this);
1647
  $this->root->tag = 'root';
1648
- $this->root->_[HDOM_INFO_BEGIN] = -1;
1649
- $this->root->nodetype = HDOM_TYPE_ROOT;
1650
  $this->parent = $this->root;
1651
  if ($this->size > 0) { $this->char = $this->doc[0]; }
1652
  }
@@ -1665,9 +1665,9 @@ class simple_html_dom
1665
  }
1666
 
1667
  // Add a text node for text between tags
1668
- $node = new simple_html_dom_node($this);
1669
  ++$this->cursor;
1670
- $node->_[HDOM_INFO_TEXT] = $s;
1671
  $this->link_nodes($node, false);
1672
  }
1673
  }
@@ -1814,7 +1814,7 @@ class simple_html_dom
1814
  {
1815
  // Set end position if no further tags found
1816
  if ($this->char !== '<') {
1817
- $this->root->_[HDOM_INFO_END] = $this->cursor;
1818
  return false;
1819
  }
1820
 
@@ -1845,7 +1845,7 @@ class simple_html_dom
1845
  if (isset($this->optional_closing_tags[$parent_lower])
1846
  && isset($this->block_tags[$tag_lower])) {
1847
 
1848
- $this->parent->_[HDOM_INFO_END] = 0;
1849
  $org_parent = $this->parent;
1850
 
1851
  // Traverse ancestors to find a matching opening tag
@@ -1864,7 +1864,7 @@ class simple_html_dom
1864
  $this->parent = $this->parent->parent;
1865
  }
1866
 
1867
- $this->parent->_[HDOM_INFO_END] = $this->cursor;
1868
  return $this->as_text_node($tag);
1869
  }
1870
  } elseif (($this->parent->parent)
@@ -1872,7 +1872,7 @@ class simple_html_dom
1872
  ) {
1873
  // Grandparent exists and current tag is a block tag, so our
1874
  // parent doesn't have an end tag
1875
- $this->parent->_[HDOM_INFO_END] = 0; // No end tag
1876
  $org_parent = $this->parent;
1877
 
1878
  // Traverse ancestors to find a matching opening tag
@@ -1886,13 +1886,13 @@ class simple_html_dom
1886
  // If we don't have a match add current tag as text node
1887
  if (strtolower($this->parent->tag) !== $tag_lower) {
1888
  $this->parent = $org_parent; // restore origonal parent
1889
- $this->parent->_[HDOM_INFO_END] = $this->cursor;
1890
  return $this->as_text_node($tag);
1891
  }
1892
  } elseif (($this->parent->parent)
1893
  && strtolower($this->parent->parent->tag) === $tag_lower
1894
  ) { // Grandparent exists and current tag closes it
1895
- $this->parent->_[HDOM_INFO_END] = 0;
1896
  $this->parent = $this->parent->parent;
1897
  } else { // Random tag, add as text node
1898
  return $this->as_text_node($tag);
@@ -1900,7 +1900,7 @@ class simple_html_dom
1900
  }
1901
 
1902
  // Set end position of parent tag to current cursor position
1903
- $this->parent->_[HDOM_INFO_END] = $this->cursor;
1904
 
1905
  if ($this->parent->parent) {
1906
  $this->parent = $this->parent->parent;
@@ -1911,8 +1911,8 @@ class simple_html_dom
1911
  }
1912
 
1913
  // start tag
1914
- $node = new simple_html_dom_node($this);
1915
- $node->_[HDOM_INFO_BEGIN] = $this->cursor;
1916
  ++$this->cursor;
1917
  $tag = $this->copy_until($this->token_slash); // Get tag name
1918
  $node->tag_start = $begin_tag_pos;
@@ -1922,17 +1922,17 @@ class simple_html_dom
1922
  // <![CDATA[ ... ]]>
1923
  // <!-- Comment -->
1924
  if (isset($tag[0]) && $tag[0] === '!') {
1925
- $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
1926
 
1927
  if (isset($tag[2]) && $tag[1] === '-' && $tag[2] === '-') { // Comment ("<!--")
1928
- $node->nodetype = HDOM_TYPE_COMMENT;
1929
  $node->tag = 'comment';
1930
  } else { // Could be doctype or CDATA but we don't care
1931
- $node->nodetype = HDOM_TYPE_UNKNOWN;
1932
  $node->tag = 'unknown';
1933
  }
1934
 
1935
- if ($this->char === '>') { $node->_[HDOM_INFO_TEXT] .= '>'; }
1936
 
1937
  $this->link_nodes($node, true);
1938
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
@@ -1943,7 +1943,7 @@ class simple_html_dom
1943
  // i.e. "<<html>"
1944
  if ($pos = strpos($tag, '<') !== false) {
1945
  $tag = '<' . substr($tag, 0, -1);
1946
- $node->_[HDOM_INFO_TEXT] = $tag;
1947
  $this->link_nodes($node, false);
1948
  $this->char = $this->doc[--$this->pos]; // prev
1949
  return true;
@@ -1951,7 +1951,7 @@ class simple_html_dom
1951
 
1952
  // Handle invalid tag names (i.e. "<html#doc>")
1953
  if (!preg_match('/^\w[\w:-]*$/', $tag)) {
1954
- $node->_[HDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
1955
 
1956
  // Next char is the beginning of a new tag, don't touch it.
1957
  if ($this->char === '<') {
@@ -1960,14 +1960,14 @@ class simple_html_dom
1960
  }
1961
 
1962
  // Next char closes current tag, add and be done with it.
1963
- if ($this->char === '>') { $node->_[HDOM_INFO_TEXT] .= '>'; }
1964
  $this->link_nodes($node, false);
1965
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1966
  return true;
1967
  }
1968
 
1969
  // begin tag, add new node
1970
- $node->nodetype = HDOM_TYPE_ELEMENT;
1971
  $tag_lower = strtolower($tag);
1972
  $node->tag = ($this->lowercase) ? $tag_lower : $tag;
1973
 
@@ -1975,7 +1975,7 @@ class simple_html_dom
1975
  if (isset($this->optional_closing_tags[$tag_lower])) {
1976
  // Traverse ancestors to close all optional closing tags
1977
  while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)])) {
1978
- $this->parent->_[HDOM_INFO_END] = 0;
1979
  $this->parent = $this->parent->parent;
1980
  }
1981
  $node->parent = $this->parent;
@@ -2005,9 +2005,9 @@ class simple_html_dom
2005
  // handle endless '<'
2006
  // Out of bounds before the tag ended
2007
  if ($this->pos >= $this->size - 1 && $this->char !== '>') {
2008
- $node->nodetype = HDOM_TYPE_TEXT;
2009
- $node->_[HDOM_INFO_END] = 0;
2010
- $node->_[HDOM_INFO_TEXT] = '<' . $tag . $space[0] . $name;
2011
  $node->tag = 'text';
2012
  $this->link_nodes($node, false);
2013
  return true;
@@ -2016,11 +2016,11 @@ class simple_html_dom
2016
  // handle mismatch '<'
2017
  // Attributes cannot start after opening tag
2018
  if ($this->doc[$this->pos - 1] == '<') {
2019
- $node->nodetype = HDOM_TYPE_TEXT;
2020
  $node->tag = 'text';
2021
  $node->attr = array();
2022
- $node->_[HDOM_INFO_END] = 0;
2023
- $node->_[HDOM_INFO_TEXT] = substr(
2024
  $this->doc,
2025
  $begin_tag_pos,
2026
  $this->pos - $begin_tag_pos - 1
@@ -2044,12 +2044,12 @@ class simple_html_dom
2044
  $this->parse_attr($node, $name, $space); // get attribute value
2045
  } else {
2046
  //no value attr: nowrap, checked selected...
2047
- $node->_[HDOM_INFO_QUOTE][] = HDOM_QUOTE_NO;
2048
  $node->attr[$name] = true;
2049
  if ($this->char != '>') { $this->char = $this->doc[--$this->pos]; } // prev
2050
  }
2051
 
2052
- $node->_[HDOM_INFO_SPACE][] = $space;
2053
 
2054
  // prepare for next attribute
2055
  $space = array(
@@ -2063,12 +2063,12 @@ class simple_html_dom
2063
  } while ($this->char !== '>' && $this->char !== '/'); // go until the tag ended
2064
 
2065
  $this->link_nodes($node, true);
2066
- $node->_[HDOM_INFO_ENDSPACE] = $space[0];
2067
 
2068
  // handle empty tags (i.e. "<div/>")
2069
  if ($this->copy_until_char('>') === '/') {
2070
- $node->_[HDOM_INFO_ENDSPACE] .= '/';
2071
- $node->_[HDOM_INFO_END] = 0;
2072
  } else {
2073
  // reset parent
2074
  if (!isset($this->self_closing_tags[strtolower($node->tag)])) {
@@ -2082,7 +2082,7 @@ class simple_html_dom
2082
  // This way when we see it in plaintext, we can generate formatting that the user wants.
2083
  // since a br tag never has sub nodes, this works well.
2084
  if ($node->tag === 'br') {
2085
- $node->_[HDOM_INFO_INNER] = $this->default_br_text;
2086
  }
2087
 
2088
  return true;
@@ -2097,19 +2097,19 @@ class simple_html_dom
2097
 
2098
  switch ($this->char) {
2099
  case '"':
2100
- $quote_type = HDOM_QUOTE_DOUBLE;
2101
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2102
  $value = $this->copy_until_char('"');
2103
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2104
  break;
2105
  case '\'':
2106
- $quote_type = HDOM_QUOTE_SINGLE;
2107
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2108
  $value = $this->copy_until_char('\'');
2109
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2110
  break;
2111
  default:
2112
- $quote_type = HDOM_QUOTE_NO;
2113
  $value = $this->copy_until($this->token_attr);
2114
  }
2115
 
@@ -2127,7 +2127,7 @@ class simple_html_dom
2127
  }
2128
 
2129
  if (!$is_duplicate) {
2130
- $node->_[HDOM_INFO_QUOTE][] = $quote_type;
2131
  $node->attr[$name] = $value;
2132
  }
2133
  }
@@ -2143,9 +2143,9 @@ class simple_html_dom
2143
 
2144
  protected function as_text_node($tag)
2145
  {
2146
- $node = new simple_html_dom_node($this);
2147
  ++$this->cursor;
2148
- $node->_[HDOM_INFO_TEXT] = '</' . $tag . '>';
2149
  $this->link_nodes($node, false);
2150
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2151
  return true;
@@ -2323,12 +2323,12 @@ class simple_html_dom
2323
 
2324
  function createElement($name, $value = null)
2325
  {
2326
- return @str_get_html("<$name>$value</$name>")->firstChild();
2327
  }
2328
 
2329
  function createTextNode($value)
2330
  {
2331
- return @end(str_get_html($value)->nodes);
2332
  }
2333
 
2334
  function getElementById($id)
26
 
27
  # mod
28
  defined('FVM_MAX_FILE_SIZE') || define('FVM_MAX_FILE_SIZE', 2000000); # Process HTML up to 2 Mb
29
+ defined('FVMDEFAULT_TARGET_CHARSET') || define('FVMDEFAULT_TARGET_CHARSET', 'UTF-8');
30
+ defined('FVMDEFAULT_BR_TEXT') || define('FVMDEFAULT_BR_TEXT', "\r\n");
31
+ defined('FVMDEFAULT_SPAN_TEXT') || define('FVMDEFAULT_SPAN_TEXT', ' ');
32
 
33
  # other
34
+ define('FVMHDOM_TYPE_ELEMENT', 1);
35
+ define('FVMHDOM_TYPE_COMMENT', 2);
36
+ define('FVMHDOM_TYPE_TEXT', 3);
37
+ define('FVMHDOM_TYPE_ENDTAG', 4);
38
+ define('FVMHDOM_TYPE_ROOT', 5);
39
+ define('FVMHDOM_TYPE_UNKNOWN', 6);
40
+ define('FVMHDOM_QUOTE_DOUBLE', 0);
41
+ define('FVMHDOM_QUOTE_SINGLE', 1);
42
+ define('FVMHDOM_QUOTE_NO', 3);
43
+ define('FVMHDOM_INFO_BEGIN', 0);
44
+ define('FVMHDOM_INFO_END', 1);
45
+ define('FVMHDOM_INFO_QUOTE', 2);
46
+ define('FVMHDOM_INFO_SPACE', 3);
47
+ define('FVMHDOM_INFO_TEXT', 4);
48
+ define('FVMHDOM_INFO_INNER', 5);
49
+ define('FVMHDOM_INFO_OUTER', 6);
50
+ define('FVMHDOM_INFO_ENDSPACE', 7);
51
+ define('FVMHDOM_SMARTY_AS_TEXT', 1);
52
 
53
  # functions
54
+ function fvm_file_get_html(
55
  $url,
56
  $use_include_path = false,
57
  $context = null,
59
  $maxLen = -1,
60
  $lowercase = true,
61
  $forceTagsClosed = true,
62
+ $target_charset = FVMDEFAULT_TARGET_CHARSET,
63
  $stripRN = true,
64
+ $defaultBRText = FVMDEFAULT_BR_TEXT,
65
+ $defaultSpanText = FVMDEFAULT_SPAN_TEXT)
66
  {
67
  if($maxLen <= 0) { $maxLen = FVM_MAX_FILE_SIZE; }
68
 
97
  return $dom->load($contents, $lowercase, $stripRN);
98
  }
99
 
100
+ function fvm_str_get_html(
101
  $str,
102
  $lowercase = true,
103
  $forceTagsClosed = true,
104
+ $target_charset = FVMDEFAULT_TARGET_CHARSET,
105
  $stripRN = true,
106
+ $defaultBRText = FVMDEFAULT_BR_TEXT,
107
+ $defaultSpanText = FVMDEFAULT_SPAN_TEXT)
108
  {
109
  $dom = new simple_html_dom(
110
  null,
124
  return $dom->load($str, $lowercase, $stripRN);
125
  }
126
 
127
+ function fvm_dump_html_tree($node, $show_attr = true, $deep = 0)
128
  {
129
  $node->dump($node);
130
  }
131
 
132
+ class fvm_simple_html_dom_node
133
  {
134
+ public $nodetype = FVMHDOM_TYPE_TEXT;
135
  public $tag = 'text';
136
  public $attr = array();
137
  public $children = array();
218
  $string .= " text: ({$this->text})";
219
  }
220
 
221
+ $string .= ' FVMHDOM_INNER_INFO: ';
222
 
223
+ if (isset($node->_[FVMHDOM_INFO_INNER])) {
224
+ $string .= "'" . $node->_[FVMHDOM_INFO_INNER] . "'";
225
  } else {
226
  $string .= ' NULL ';
227
  }
345
 
346
  function innertext()
347
  {
348
+ if (isset($this->_[FVMHDOM_INFO_INNER])) {
349
+ return $this->_[FVMHDOM_INFO_INNER];
350
  }
351
 
352
+ if (isset($this->_[FVMHDOM_INFO_TEXT])) {
353
+ return $this->dom->restore_noise($this->_[FVMHDOM_INFO_TEXT]);
354
  }
355
 
356
  $ret = '';
387
  call_user_func_array($this->dom->callback, array($this));
388
  }
389
 
390
+ if (isset($this->_[FVMHDOM_INFO_OUTER])) {
391
+ return $this->_[FVMHDOM_INFO_OUTER];
392
  }
393
 
394
+ if (isset($this->_[FVMHDOM_INFO_TEXT])) {
395
+ return $this->dom->restore_noise($this->_[FVMHDOM_INFO_TEXT]);
396
  }
397
 
398
  $ret = '';
399
 
400
+ if ($this->dom && $this->dom->nodes[$this->_[FVMHDOM_INFO_BEGIN]]) {
401
+ $ret = $this->dom->nodes[$this->_[FVMHDOM_INFO_BEGIN]]->makeup();
402
  }
403
 
404
+ if (isset($this->_[FVMHDOM_INFO_INNER])) {
405
+ // todo: <br> should either never have FVMHDOM_INFO_INNER or always
406
  if ($this->tag !== 'br') {
407
+ $ret .= $this->_[FVMHDOM_INFO_INNER];
408
  }
409
  } elseif ($this->nodes) {
410
  foreach ($this->nodes as $n) {
412
  }
413
  }
414
 
415
+ if (isset($this->_[FVMHDOM_INFO_END]) && $this->_[FVMHDOM_INFO_END] != 0) {
416
  $ret .= '</' . $this->tag . '>';
417
  }
418
 
421
 
422
  function text()
423
  {
424
+ if (isset($this->_[FVMHDOM_INFO_INNER])) {
425
+ return $this->_[FVMHDOM_INFO_INNER];
426
  }
427
 
428
  switch ($this->nodetype) {
429
+ case FVMHDOM_TYPE_TEXT: return $this->dom->restore_noise($this->_[FVMHDOM_INFO_TEXT]);
430
+ case FVMHDOM_TYPE_COMMENT: return '';
431
+ case FVMHDOM_TYPE_UNKNOWN: return '';
432
  }
433
 
434
  if (strcasecmp($this->tag, 'script') === 0) { return ''; }
436
 
437
  $ret = '';
438
 
439
+ // In rare cases, (always node type 1 or FVMHDOM_TYPE_ELEMENT - observed
440
  // for some span tags, and some p tags) $this->nodes is set to NULL.
441
  // NOTE: This indicates that there is a problem where it's set to NULL
442
  // without a clear happening.
472
  function makeup()
473
  {
474
  // text, comment, unknown
475
+ if (isset($this->_[FVMHDOM_INFO_TEXT])) {
476
+ return $this->dom->restore_noise($this->_[FVMHDOM_INFO_TEXT]);
477
  }
478
 
479
  $ret = '<' . $this->tag;
485
  // skip removed attribute
486
  if ($val === null || $val === false) { continue; }
487
 
488
+ $ret .= $this->_[FVMHDOM_INFO_SPACE][$i][0];
489
 
490
  //no value attr: nowrap, checked selected...
491
  if ($val === true) {
492
  $ret .= $key;
493
  } else {
494
+ switch ($this->_[FVMHDOM_INFO_QUOTE][$i])
495
  {
496
+ case FVMHDOM_QUOTE_DOUBLE: $quote = '"'; break;
497
+ case FVMHDOM_QUOTE_SINGLE: $quote = '\''; break;
498
  default: $quote = '';
499
  }
500
 
501
  $ret .= $key
502
+ . $this->_[FVMHDOM_INFO_SPACE][$i][1]
503
  . '='
504
+ . $this->_[FVMHDOM_INFO_SPACE][$i][2]
505
  . $quote
506
  . $val
507
  . $quote;
509
  }
510
 
511
  $ret = $this->dom->restore_noise($ret);
512
+ return $ret . $this->_[FVMHDOM_INFO_ENDSPACE] . '>';
513
  }
514
 
515
  function find($selector, $idx = null, $lowercase = false)
524
  // code tracker id 2788009
525
  // used to be: if (($levle=count($selectors[0]))===0) return array();
526
  if (($levle = count($selectors[$c])) === 0) { return array(); }
527
+ if (!isset($this->_[FVMHDOM_INFO_BEGIN])) { return array(); }
528
 
529
+ $head = array($this->_[FVMHDOM_INFO_BEGIN] => 1);
530
  $cmd = ' '; // Combinator
531
 
532
  // handle descendant selectors, no recursive!
575
  if ($parent_cmd === ' ') { // Descendant Combinator
576
  // Find parent closing tag if the current element doesn't have a closing
577
  // tag (i.e. void element)
578
+ $end = (!empty($this->_[FVMHDOM_INFO_END])) ? $this->_[FVMHDOM_INFO_END] : 0;
579
  if ($end == 0) {
580
  $parent = $this->parent;
581
+ while (!isset($parent->_[FVMHDOM_INFO_END]) && $parent !== null) {
582
  $end -= 1;
583
  $parent = $parent->parent;
584
  }
585
+ $end += $parent->_[FVMHDOM_INFO_END];
586
  }
587
 
588
  // Get list of target nodes
589
+ $nodes_start = $this->_[FVMHDOM_INFO_BEGIN] + 1;
590
  $nodes_count = $end - $nodes_start;
591
  $nodes = array_slice($this->dom->nodes, $nodes_start, $nodes_count, true);
592
  } elseif ($parent_cmd === '>') { // Child Combinator
777
  }
778
 
779
  // Found a match. Add to list and clear node
780
+ if ($pass) $ret[$node->_[FVMHDOM_INFO_BEGIN]] = 1;
781
  unset($node);
782
  }
783
  // It's passed by reference so this is actually what this function returns.
990
  if (is_object($debug_object)) { $debug_object->debug_log_entry(1); }
991
 
992
  switch ($name) {
993
+ case 'outertext': return $this->_[FVMHDOM_INFO_OUTER] = $value;
994
  case 'innertext':
995
+ if (isset($this->_[FVMHDOM_INFO_TEXT])) {
996
+ return $this->_[FVMHDOM_INFO_TEXT] = $value;
997
  }
998
+ return $this->_[FVMHDOM_INFO_INNER] = $value;
999
  }
1000
 
1001
  if (!isset($this->attr[$name])) {
1002
+ $this->_[FVMHDOM_INFO_SPACE][] = array(' ', '', '');
1003
+ $this->_[FVMHDOM_INFO_QUOTE][] = FVMHDOM_QUOTE_DOUBLE;
1004
  }
1005
 
1006
  $this->attr[$name] = $value;
1467
  $str = null,
1468
  $lowercase = true,
1469
  $forceTagsClosed = true,
1470
+ $target_charset = FVMDEFAULT_TARGET_CHARSET,
1471
  $stripRN = true,
1472
+ $defaultBRText = FVMDEFAULT_BR_TEXT,
1473
+ $defaultSpanText = FVMDEFAULT_SPAN_TEXT,
1474
  $options = 0)
1475
  {
1476
  if ($str) {
1505
  $str,
1506
  $lowercase = true,
1507
  $stripRN = true,
1508
+ $defaultBRText = FVMDEFAULT_BR_TEXT,
1509
+ $defaultSpanText = FVMDEFAULT_SPAN_TEXT,
1510
  $options = 0)
1511
  {
1512
  global $debug_object;
1541
  // strip out server side scripts
1542
  $this->remove_noise("'(<\?)(.*?)(\?>)'s", true);
1543
 
1544
+ if($options & FVMHDOM_SMARTY_AS_TEXT) { // Strip Smarty scripts
1545
  $this->remove_noise("'(\{\w)(.*?)(\})'s", true);
1546
  }
1547
 
1548
  // parsing
1549
  $this->parse();
1550
  // end
1551
+ $this->root->_[FVMHDOM_INFO_END] = $this->cursor;
1552
  $this->parse_charset();
1553
 
1554
  // make load function chainable
1628
 
1629
  protected function prepare(
1630
  $str, $lowercase = true,
1631
+ $defaultBRText = FVMDEFAULT_BR_TEXT,
1632
+ $defaultSpanText = FVMDEFAULT_SPAN_TEXT)
1633
  {
1634
  $this->clear();
1635
 
1643
  $this->lowercase = $lowercase;
1644
  $this->default_br_text = $defaultBRText;
1645
  $this->default_span_text = $defaultSpanText;
1646
+ $this->root = new fvm_simple_html_dom_node($this);
1647
  $this->root->tag = 'root';
1648
+ $this->root->_[FVMHDOM_INFO_BEGIN] = -1;
1649
+ $this->root->nodetype = FVMHDOM_TYPE_ROOT;
1650
  $this->parent = $this->root;
1651
  if ($this->size > 0) { $this->char = $this->doc[0]; }
1652
  }
1665
  }
1666
 
1667
  // Add a text node for text between tags
1668
+ $node = new fvm_simple_html_dom_node($this);
1669
  ++$this->cursor;
1670
+ $node->_[FVMHDOM_INFO_TEXT] = $s;
1671
  $this->link_nodes($node, false);
1672
  }
1673
  }
1814
  {
1815
  // Set end position if no further tags found
1816
  if ($this->char !== '<') {
1817
+ $this->root->_[FVMHDOM_INFO_END] = $this->cursor;
1818
  return false;
1819
  }
1820
 
1845
  if (isset($this->optional_closing_tags[$parent_lower])
1846
  && isset($this->block_tags[$tag_lower])) {
1847
 
1848
+ $this->parent->_[FVMHDOM_INFO_END] = 0;
1849
  $org_parent = $this->parent;
1850
 
1851
  // Traverse ancestors to find a matching opening tag
1864
  $this->parent = $this->parent->parent;
1865
  }
1866
 
1867
+ $this->parent->_[FVMHDOM_INFO_END] = $this->cursor;
1868
  return $this->as_text_node($tag);
1869
  }
1870
  } elseif (($this->parent->parent)
1872
  ) {
1873
  // Grandparent exists and current tag is a block tag, so our
1874
  // parent doesn't have an end tag
1875
+ $this->parent->_[FVMHDOM_INFO_END] = 0; // No end tag
1876
  $org_parent = $this->parent;
1877
 
1878
  // Traverse ancestors to find a matching opening tag
1886
  // If we don't have a match add current tag as text node
1887
  if (strtolower($this->parent->tag) !== $tag_lower) {
1888
  $this->parent = $org_parent; // restore origonal parent
1889
+ $this->parent->_[FVMHDOM_INFO_END] = $this->cursor;
1890
  return $this->as_text_node($tag);
1891
  }
1892
  } elseif (($this->parent->parent)
1893
  && strtolower($this->parent->parent->tag) === $tag_lower
1894
  ) { // Grandparent exists and current tag closes it
1895
+ $this->parent->_[FVMHDOM_INFO_END] = 0;
1896
  $this->parent = $this->parent->parent;
1897
  } else { // Random tag, add as text node
1898
  return $this->as_text_node($tag);
1900
  }
1901
 
1902
  // Set end position of parent tag to current cursor position
1903
+ $this->parent->_[FVMHDOM_INFO_END] = $this->cursor;
1904
 
1905
  if ($this->parent->parent) {
1906
  $this->parent = $this->parent->parent;
1911
  }
1912
 
1913
  // start tag
1914
+ $node = new fvm_simple_html_dom_node($this);
1915
+ $node->_[FVMHDOM_INFO_BEGIN] = $this->cursor;
1916
  ++$this->cursor;
1917
  $tag = $this->copy_until($this->token_slash); // Get tag name
1918
  $node->tag_start = $begin_tag_pos;
1922
  // <![CDATA[ ... ]]>
1923
  // <!-- Comment -->
1924
  if (isset($tag[0]) && $tag[0] === '!') {
1925
+ $node->_[FVMHDOM_INFO_TEXT] = '<' . $tag . $this->copy_until_char('>');
1926
 
1927
  if (isset($tag[2]) && $tag[1] === '-' && $tag[2] === '-') { // Comment ("<!--")
1928
+ $node->nodetype = FVMHDOM_TYPE_COMMENT;
1929
  $node->tag = 'comment';
1930
  } else { // Could be doctype or CDATA but we don't care
1931
+ $node->nodetype = FVMHDOM_TYPE_UNKNOWN;
1932
  $node->tag = 'unknown';
1933
  }
1934
 
1935
+ if ($this->char === '>') { $node->_[FVMHDOM_INFO_TEXT] .= '>'; }
1936
 
1937
  $this->link_nodes($node, true);
1938
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1943
  // i.e. "<<html>"
1944
  if ($pos = strpos($tag, '<') !== false) {
1945
  $tag = '<' . substr($tag, 0, -1);
1946
+ $node->_[FVMHDOM_INFO_TEXT] = $tag;
1947
  $this->link_nodes($node, false);
1948
  $this->char = $this->doc[--$this->pos]; // prev
1949
  return true;
1951
 
1952
  // Handle invalid tag names (i.e. "<html#doc>")
1953
  if (!preg_match('/^\w[\w:-]*$/', $tag)) {
1954
+ $node->_[FVMHDOM_INFO_TEXT] = '<' . $tag . $this->copy_until('<>');
1955
 
1956
  // Next char is the beginning of a new tag, don't touch it.
1957
  if ($this->char === '<') {
1960
  }
1961
 
1962
  // Next char closes current tag, add and be done with it.
1963
+ if ($this->char === '>') { $node->_[FVMHDOM_INFO_TEXT] .= '>'; }
1964
  $this->link_nodes($node, false);
1965
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
1966
  return true;
1967
  }
1968
 
1969
  // begin tag, add new node
1970
+ $node->nodetype = FVMHDOM_TYPE_ELEMENT;
1971
  $tag_lower = strtolower($tag);
1972
  $node->tag = ($this->lowercase) ? $tag_lower : $tag;
1973
 
1975
  if (isset($this->optional_closing_tags[$tag_lower])) {
1976
  // Traverse ancestors to close all optional closing tags
1977
  while (isset($this->optional_closing_tags[$tag_lower][strtolower($this->parent->tag)])) {
1978
+ $this->parent->_[FVMHDOM_INFO_END] = 0;
1979
  $this->parent = $this->parent->parent;
1980
  }
1981
  $node->parent = $this->parent;
2005
  // handle endless '<'
2006
  // Out of bounds before the tag ended
2007
  if ($this->pos >= $this->size - 1 && $this->char !== '>') {
2008
+ $node->nodetype = FVMHDOM_TYPE_TEXT;
2009
+ $node->_[FVMHDOM_INFO_END] = 0;
2010
+ $node->_[FVMHDOM_INFO_TEXT] = '<' . $tag . $space[0] . $name;
2011
  $node->tag = 'text';
2012
  $this->link_nodes($node, false);
2013
  return true;
2016
  // handle mismatch '<'
2017
  // Attributes cannot start after opening tag
2018
  if ($this->doc[$this->pos - 1] == '<') {
2019
+ $node->nodetype = FVMHDOM_TYPE_TEXT;
2020
  $node->tag = 'text';
2021
  $node->attr = array();
2022
+ $node->_[FVMHDOM_INFO_END] = 0;
2023
+ $node->_[FVMHDOM_INFO_TEXT] = substr(
2024
  $this->doc,
2025
  $begin_tag_pos,
2026
  $this->pos - $begin_tag_pos - 1
2044
  $this->parse_attr($node, $name, $space); // get attribute value
2045
  } else {
2046
  //no value attr: nowrap, checked selected...
2047
+ $node->_[FVMHDOM_INFO_QUOTE][] = FVMHDOM_QUOTE_NO;
2048
  $node->attr[$name] = true;
2049
  if ($this->char != '>') { $this->char = $this->doc[--$this->pos]; } // prev
2050
  }
2051
 
2052
+ $node->_[FVMHDOM_INFO_SPACE][] = $space;
2053
 
2054
  // prepare for next attribute
2055
  $space = array(
2063
  } while ($this->char !== '>' && $this->char !== '/'); // go until the tag ended
2064
 
2065
  $this->link_nodes($node, true);
2066
+ $node->_[FVMHDOM_INFO_ENDSPACE] = $space[0];
2067
 
2068
  // handle empty tags (i.e. "<div/>")
2069
  if ($this->copy_until_char('>') === '/') {
2070
+ $node->_[FVMHDOM_INFO_ENDSPACE] .= '/';
2071
+ $node->_[FVMHDOM_INFO_END] = 0;
2072
  } else {
2073
  // reset parent
2074
  if (!isset($this->self_closing_tags[strtolower($node->tag)])) {
2082
  // This way when we see it in plaintext, we can generate formatting that the user wants.
2083
  // since a br tag never has sub nodes, this works well.
2084
  if ($node->tag === 'br') {
2085
+ $node->_[FVMHDOM_INFO_INNER] = $this->default_br_text;
2086
  }
2087
 
2088
  return true;
2097
 
2098
  switch ($this->char) {
2099
  case '"':
2100
+ $quote_type = FVMHDOM_QUOTE_DOUBLE;
2101
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2102
  $value = $this->copy_until_char('"');
2103
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2104
  break;
2105
  case '\'':
2106
+ $quote_type = FVMHDOM_QUOTE_SINGLE;
2107
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2108
  $value = $this->copy_until_char('\'');
2109
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2110
  break;
2111
  default:
2112
+ $quote_type = FVMHDOM_QUOTE_NO;
2113
  $value = $this->copy_until($this->token_attr);
2114
  }
2115
 
2127
  }
2128
 
2129
  if (!$is_duplicate) {
2130
+ $node->_[FVMHDOM_INFO_QUOTE][] = $quote_type;
2131
  $node->attr[$name] = $value;
2132
  }
2133
  }
2143
 
2144
  protected function as_text_node($tag)
2145
  {
2146
+ $node = new fvm_simple_html_dom_node($this);
2147
  ++$this->cursor;
2148
+ $node->_[FVMHDOM_INFO_TEXT] = '</' . $tag . '>';
2149
  $this->link_nodes($node, false);
2150
  $this->char = (++$this->pos < $this->size) ? $this->doc[$this->pos] : null; // next
2151
  return true;
2323
 
2324
  function createElement($name, $value = null)
2325
  {
2326
+ return @fvm_str_get_html("<$name>$value</$name>")->firstChild();
2327
  }
2328
 
2329
  function createTextNode($value)
2330
  {
2331
+ return @end(fvm_str_get_html($value)->nodes);
2332
  }
2333
 
2334
  function getElementById($id)
readme.txt CHANGED
@@ -3,13 +3,13 @@ Contributors: Alignak
3
  Tags: PHP Minify, Lighthouse, GTmetrix, Pingdom, Pagespeed, Merging, Minification, Optimization, Speed, Performance, FVM
4
  Requires at least: 4.9
5
  Requires PHP: 5.6
6
- Stable tag: 3.2.2
7
- Tested up to: 5.7.1
8
  Text Domain: fast-velocity-minify
9
  License: GPLv3 or later
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
12
- 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.
13
 
14
 
15
  == Description ==
@@ -49,8 +49,18 @@ You need a public directory to store and serve minified cache files. If you need
49
 
50
  == Changelog ==
51
 
52
- = 3.2.2 [2021.05.09] =
 
 
 
 
 
 
 
53
  * added auto varnish cache purge for Cloudways
 
 
 
54
  * fixed some JS files not being minified
55
 
56
  = 3.2.1 [2021.05.07] =
3
  Tags: PHP Minify, Lighthouse, GTmetrix, Pingdom, Pagespeed, Merging, Minification, Optimization, Speed, Performance, FVM
4
  Requires at least: 4.9
5
  Requires PHP: 5.6
6
+ Stable tag: 3.2.4
7
+ Tested up to: 5.9.1
8
  Text Domain: fast-velocity-minify
9
  License: GPLv3 or later
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
12
+ 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.
13
 
14
 
15
  == Description ==
49
 
50
  == Changelog ==
51
 
52
+ = 3.2.4 [2022.01.31] =
53
+ * WP 5.9 / PHP 8 maintenance release
54
+ * changed deferred css/js cache clearing from 24h to 7 days
55
+ * added cache purging support for nginx helper plugin
56
+ * added option to allow processing on specific query strings
57
+ * other bug fixes
58
+
59
+ = 3.2.3 [2021.05.15] =
60
  * added auto varnish cache purge for Cloudways
61
+ * switched from WP_Filesystem_Direct() to WP_Filesystem()
62
+
63
+ = 3.2.2 [2021.05.09] =
64
  * fixed some JS files not being minified
65
 
66
  = 3.2.1 [2021.05.07] =