Guest User

Untitled

a guest
Jan 24th, 2023
48
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 39.94 KB | None | 0 0
  1. <?php
  2. /*
  3. Description: Allows for asset generation/inclusion like css files and js. Also allows to combine files.
  4. Basic idea: allows us to enqueue scripts and styles and before the files get enqueued individually,
  5. we try to generate a compressed version and enqueue this one instead.
  6.  
  7. Author: Kriesi
  8. Since: 4.2.4
  9. */
  10. if( ! defined( 'ABSPATH' ) ) { exit; } // Don't load directly
  11.  
  12. if ( ! class_exists( 'aviaAssetManager' ) )
  13. {
  14. class aviaAssetManager extends aviaBuilder\base\object_properties
  15. {
  16. /**
  17. * the database prefix we use to store the script name
  18. *
  19. * @since 4.2.4
  20. * @var string
  21. */
  22. protected $db_prefix;
  23.  
  24. /**
  25. * which files to include by default (all, none, all theme files, only module files)
  26. *
  27. * @since 4.2.4
  28. * @var array
  29. */
  30. protected $which_files;
  31.  
  32. /**
  33. * type of files to compress
  34. *
  35. * @since 4.2.4
  36. * @var array
  37. */
  38. protected $compress_files;
  39.  
  40. /**
  41. * files to exclude
  42. *
  43. * @since 4.2.4
  44. * @var array
  45. */
  46. protected $exclude_files;
  47.  
  48. /**
  49. * collect all files to deregister
  50. *
  51. * @since 4.2.4
  52. * @var array
  53. */
  54. protected $deregister_files;
  55.  
  56. /**
  57. * files that will be removed from queue and instead ONLY printed to the head of the page, no matter if they are actually set to be minified
  58. *
  59. * @since 4.2.4
  60. * @var array
  61. */
  62. protected $force_print_to_head;
  63.  
  64. /**
  65. * files that will be printed to the head in addition to staying enqueued if they are enqueed.
  66. * Helps with above the fold render blocking
  67. *
  68. * @since 4.2.4
  69. * @var array
  70. */
  71. protected $additional_print_to_head;
  72.  
  73. /**
  74. * file that will be added to the compression, even if the $whichfiles setting does not include them
  75. *
  76. * @since 4.2.4
  77. * @var array
  78. */
  79. protected $force_include;
  80.  
  81. /**
  82. *
  83. * @since 5.3
  84. * @var array
  85. */
  86. protected $base_url;
  87.  
  88. /**
  89. * if true the files get regenerated at each page request
  90. *
  91. * @since 4.2.4
  92. * @var boolean
  93. */
  94. protected $testmode;
  95.  
  96. /**
  97. *
  98. * @since 4.2.4
  99. */
  100. public function __construct()
  101. {
  102. $this->db_prefix = 'aviaAsset_';
  103.  
  104. $this->which_files = array(
  105. 'css' => 'avia-module',
  106. 'js' => 'avia-module'
  107. );
  108.  
  109. $this->compress_files = array(
  110. 'css' => true,
  111. 'js' => true
  112. );
  113.  
  114. $this->exclude_files = array(
  115. 'css' => array( 'admin-bar', 'dashicons' ),
  116. 'js' => array( 'jquery-core', 'admin-bar','comment-reply' )
  117. );
  118.  
  119. $this->deregister_files = array(
  120. 'css' => array(),
  121. 'js' => array()
  122. );
  123.  
  124. $this->force_print_to_head = array(
  125. 'css' => array( 'layerslider', 'layerslider-front' ),
  126. 'js' => array( 'avia-compat' )
  127. );
  128.  
  129. $this->additional_print_to_head = array(
  130. 'css' => array(),
  131. 'js' => array()
  132. );
  133.  
  134. $this->force_include = array(
  135. 'css' => array(),
  136. 'js' => array( 'wp-embed' )
  137. );
  138.  
  139. $this->base_url = '';
  140. $this->testmode = false;
  141.  
  142. /**
  143. * allow to change the files that should be merged:
  144. * 'none' => merging is deactivated
  145. * 'avia-module' => only module files
  146. * 'avia' => all framework files
  147. * 'all' => all enqueued files
  148. *
  149. * @since 4.2.4
  150. * @param array $this->which_files
  151. * @return array
  152. */
  153. $this->which_files = apply_filters( 'avf_merge_assets', $this->which_files );
  154.  
  155. /**
  156. * allow to change the files that should be compressed:
  157. * true and false allowed
  158. *
  159. * @since 4.2.4
  160. * @param array $this->compress_files
  161. * @return array
  162. */
  163. $this->compress_files = apply_filters( 'avf_compress_assets', $this->compress_files );
  164.  
  165. /**
  166. * files that are always excluded like admin bar files
  167. * files that are processed are added to this list as well, in case another file should be generated
  168. *
  169. * @since 4.2.4
  170. * @param array $this->exclude_files
  171. * @return array
  172. */
  173. $this->exclude_files = apply_filters( 'avf_exclude_assets', $this->exclude_files );
  174.  
  175. /**
  176. * filter to add file to the list that are force included inline in the head if they are loaded on the page
  177. *
  178. * @since 4.2.4
  179. * @param array $this->force_print_to_head
  180. * @return array
  181. */
  182. $this->force_print_to_head = apply_filters( 'avf_force_print_asset_to_head', $this->force_print_to_head );
  183.  
  184. /**
  185. * filter that allows you to tell the script to also print an asset instead of only enqueuing it
  186. *
  187. * @since 4.2.4
  188. * @param array $this->additional_print_to_head
  189. * @return array
  190. */
  191. $this->additional_print_to_head = apply_filters( 'avf_also_print_asset', $this->additional_print_to_head );
  192.  
  193. /**
  194. * force include an asset, even if the $whichfiles setting does not include it
  195. *
  196. * @since 4.2.4
  197. * @param array $this->force_include
  198. * @return array
  199. */
  200. $this->force_include = apply_filters( 'avf_force_include_asset', $this->force_include );
  201.  
  202.  
  203. //before enqueuing the css and js files check if we can serve a compressed version (only frontend)
  204. add_action( 'wp_enqueue_scripts', array( $this, 'try_minifying_scripts' ), 999999 );
  205.  
  206. add_action( 'wp_print_footer_scripts', array( $this, 'minimize_footer_scripts' ), 9 );
  207.  
  208. //if we got any compressed/combined scripts remove the default registered files
  209. //add_action('wp_enqueue_scripts', array(&$this, 'try_deregister_scripts') , 9999999 );
  210.  
  211. //print assets that are set for printing
  212. add_action( 'wp_head', array( $this, 'inline_print_assets' ), 20 );
  213. }
  214.  
  215. /**
  216. * @since 5.3
  217. */
  218. public function __destruct()
  219. {
  220. unset( $this->which_files );
  221. unset( $this->compress_files );
  222. unset( $this->exclude_files );
  223. unset( $this->deregister_files );
  224. unset( $this->force_print_to_head );
  225. unset( $this->additional_print_to_head );
  226. unset( $this->force_include );
  227. }
  228.  
  229. /**
  230. * default calling of the merging/compression script.
  231. * the 'merge' function can in theory also be called from outside
  232. *
  233. * @since 4.2.4
  234. */
  235. public function try_minifying_scripts()
  236. {
  237. // check if we got a compressed version that includes all the elements that we need.
  238. // generate a new version if the theme version changes or the css files that are included change
  239. // compare by hash if a new version is required
  240.  
  241.  
  242. //compresses css files and stores them in a file called avia-merged-styles-HASH_ID.css
  243. $this->merge( 'css', 'avia-merged-styles' );
  244.  
  245. //compresses JS files and stores them in a file called avia-head-scripts-HASH_ID.js/avia-footer-scripts-HASH_ID.js// - footer scripts attr: (group 1)
  246. $this->merge( 'js', 'avia-head-scripts', array( 'groups' => 0 ) );
  247. }
  248.  
  249. /**
  250. * extra hook that allows us to minimze scripts that were added to be printed in wp_footer
  251. *
  252. * @since 4.2.4
  253. */
  254. public function minimize_footer_scripts()
  255. {
  256. $this->merge( 'js', 'avia-footer-scripts', array( 'groups' => 1 ) );
  257. }
  258.  
  259. /**
  260. * Checks if we can merge a group of files based on the passed params
  261. *
  262. * @since 4.2.4
  263. * @param string $file_type 'css' | 'js'
  264. * @param string $file_group_name
  265. * @param array $conditions
  266. * @return void
  267. */
  268. protected function merge( $file_type, $file_group_name, $conditions = array() )
  269. {
  270. if( ! isset( $this->which_files[ $file_type ] ) || $this->which_files[ $file_type ] == 'none' )
  271. {
  272. return;
  273. }
  274.  
  275. //hook into all enqueued styles
  276. global $wp_styles, $wp_scripts;
  277.  
  278. //get the data of the file we would generate
  279. $enqueued = ( $file_type == 'css' ) ? $wp_styles : $wp_scripts;
  280. $data = $this->get_file_data( $file_type , $file_group_name , $enqueued , $conditions );
  281.  
  282.  
  283. /**
  284. * Check if we got a db entry with this hash. if so, no further merging needed and we can remove the registered files from the enque array.
  285. *
  286. * A problem occurs that if user makes changes to file content without changing theme version numbers or adding/removing files:
  287. * the same hash is returned and browsers do not recognise the changing until their cache expires.
  288. * Therefore we add a timestamp to each new generated hash.
  289. */
  290. $generated_files = get_option( $this->db_prefix . $file_group_name );
  291. if( ! is_array( $generated_files ) )
  292. {
  293. $generated_files = array();
  294. }
  295.  
  296. $exists = false;
  297.  
  298. foreach( $generated_files as $generated_file => $value )
  299. {
  300. if( 'error-generating-file' === $value )
  301. {
  302. continue;
  303. }
  304.  
  305. $split = strpos( $generated_file, '---' );
  306.  
  307. /**
  308. * Fallback for possible old files - in this case we use this
  309. */
  310. if( false === $split )
  311. {
  312. if( $generated_file == $data['hash'] )
  313. {
  314. $exists = true;
  315. break;
  316. }
  317. }
  318. else
  319. {
  320. $filtered = substr( $generated_file, 0, $split );
  321. if( $filtered == $data['hash'] )
  322. {
  323. $exists = true;
  324. $data['hash'] = $generated_file;
  325. break;
  326. }
  327. }
  328. }
  329.  
  330. // if the file does not exist try to generate it
  331. if( ! $exists || $this->testmode )
  332. {
  333. /**
  334. * @since 4.7.3.1
  335. */
  336. $uniqid = ( false === strpos( avia_get_option( 'merge_disable_unique_timestamp' ), 'disable_unique_timestamp' ) ) ? uniqid() : '';
  337.  
  338. /**
  339. * Some server configurations seem to cache WP options and do not return changed options - so we generate a new file again and again
  340. * This filter allows to return the same value (or a custom value) for each file. '---' is added to seperate and identify as added value.
  341. * Return empty string to avoid adding.
  342. *
  343. * @since 4.7.2.1
  344. * @param string
  345. * @param array $data
  346. * @param WP_Scripts $enqueued
  347. * @param string $file_group_name
  348. * @param array $conditions
  349. * @return string
  350. */
  351. $uniqid = apply_filters( 'avf_merged_files_unique_id', $uniqid, $file_type, $data, $enqueued, $file_group_name, $conditions );
  352.  
  353. if( ! empty( $uniqid ) )
  354. {
  355. $data['hash'] = $data['hash'] . '---' . trim( $uniqid );
  356. }
  357.  
  358. $generated_files[ $data['hash'] ] = $this->generate_file( $file_type, $data, $enqueued );
  359. }
  360.  
  361. //if the file exists and was properly generated at one time in the past, enqueue the new file and remove all the others. otherwise do nothing
  362. if( $generated_files[ $data['hash'] ] && $generated_files[ $data['hash'] ] !== 'error-generating-file' )
  363. {
  364. if( is_array( $data['remove'] ) )
  365. {
  366. foreach( $data['remove'] as $remove )
  367. {
  368. //for future iterations, exlude all files we used here
  369. $this->exclude_files[ $file_type ][] = $remove['name'];
  370.  
  371. //if we know the file content deregister it, otherwise dont do it. might be that the file was not readable
  372. if( $remove['file_content'] !== false )
  373. {
  374. $this->deregister_files[ $file_type ][] = $remove['name'];
  375. }
  376. }
  377. }
  378.  
  379. $avia_dyn_file_url = $this->get_file_url($data, $file_type);
  380.  
  381. //if file exists enqueue it
  382. if( $file_type == 'css' )
  383. {
  384. wp_enqueue_style( $file_group_name , $avia_dyn_file_url, array(), null, 'all' );
  385. }
  386. else
  387. {
  388. $footer = isset( $conditions['groups'] ) ? $conditions['groups'] : true;
  389. wp_enqueue_script( $file_group_name, $avia_dyn_file_url, array(), null, $footer );
  390. }
  391.  
  392. }
  393. else
  394. //if the file was not generated because it was empty there is a chance that it is empty because we want all the script to be printed inline
  395. //therefore we need to make sure that the force_print_to_head array is checked
  396. {
  397.  
  398. if( is_array( $data['remove'] ) )
  399. {
  400. foreach( $data['remove'] as $remove )
  401. {
  402. if( $remove['print'] === true )
  403. {
  404. //for future iterations, exlude all files we used here
  405. $this->exclude_files[ $file_type ][] = $remove['name'];
  406. $this->deregister_files[ $file_type ][] = $remove['name'];
  407. }
  408. }
  409. }
  410. }
  411.  
  412. /**
  413. * store that we tried to generate the file but it did not work.
  414. * Therefore no more future tries but simple enqueuing of the single files.
  415. */
  416. if( empty( $generated_files[ $data['hash'] ] ) )
  417. {
  418. /**
  419. * Remove unique timestamp again to avoid multiple entries in db.
  420. * Saving theme options will clean up option - only modifying will leave a single entry only for each hash
  421. *
  422. * @since 4.7.5.1
  423. */
  424. unset( $generated_files[ $data['hash'] ] );
  425. $split = strpos( $data['hash'], '---' );
  426. $hash = false !== $split ? substr( $data['hash'], 0, $split ) : $data['hash'];
  427.  
  428. $generated_files[ $hash ] = 'error-generating-file';
  429. $this->update_option_fix_cache( $this->db_prefix . $file_group_name, $generated_files );
  430. }
  431.  
  432. //deregister everything that was compressed
  433. $this->try_deregister_scripts( $file_group_name );
  434.  
  435. }
  436.  
  437. /**
  438. *
  439. * @since 4.2.4
  440. * @param array $data
  441. * @param string $file_type
  442. * @return string
  443. */
  444. protected function get_file_url( $data, $file_type )
  445. {
  446. global $avia_config;
  447.  
  448. $avia_upload_dir = wp_upload_dir();
  449. if( is_ssl() )
  450. {
  451. $avia_upload_dir['baseurl'] = str_replace( 'http://', 'https://', $avia_upload_dir['baseurl'] );
  452. }
  453.  
  454. $url = $avia_upload_dir['baseurl'] . trailingslashit( $avia_config['dynamic_files_upload_folder'] ) . $data['hash'] . '.' . $file_type;
  455.  
  456. return $url;
  457. }
  458.  
  459. /**
  460. * returns a file data array with hash, version number and scripts we need to dequeue.
  461. * the hash we generate consists of parent theme version, child theme version and files to include. if any of this changes we create a new file
  462. *
  463. * @since 4.2.4
  464. * @param string $file_type
  465. * @param string $file_group_name
  466. * @param WP_Scripts $enqueued enqueued styles
  467. * @param array $conditions
  468. * @return string
  469. */
  470. protected function get_file_data( $file_type, $file_group_name, $enqueued, $conditions )
  471. {
  472. $data = array(
  473. 'hash' => '' ,
  474. 'version' => '' ,
  475. 'remove' => array(),
  476. 'file_group_name' => $file_group_name
  477. );
  478.  
  479. //generate the version number
  480. $theme = wp_get_theme();
  481. $data['version'] = $theme->get( 'Version' );
  482.  
  483. if( false !== $theme->parent() )
  484. {
  485. $theme = $theme->parent();
  486. $data['version'] = $theme->get( 'Version' ) . '-' . $data['version'];
  487. }
  488.  
  489. //set up the to_do array which has the proper dependencies
  490. $enqueued->all_deps( $enqueued->queue );
  491.  
  492.  
  493. //stored files in the db
  494. $stored_assets = get_option( $this->db_prefix . $file_type . '_filecontent' );
  495. if( empty( $stored_assets ) )
  496. {
  497. $stored_assets = array();
  498. }
  499.  
  500. //generate the name string for all the files included. store the data of those files so we can properly include them later and then dequeue them
  501. foreach( $enqueued->to_do as $enqueued_index => $file )
  502. {
  503. $force_print = in_array( $file, $this->force_print_to_head[ $file_type ] );
  504.  
  505. // check which files to include based on the $which_files setting (all, none, modules only, all framework files)
  506. if( ( 'all' == $this->which_files[ $file_type ] ) ||
  507. ( 'avia-module' == $this->which_files[ $file_type ] && strpos( $file, 'avia-module' ) !== false ) ||
  508. ( 'avia' == $this->which_files[ $file_type ] && strpos( $file, 'avia' ) !== false ) ||
  509. ( $force_print ) ||
  510. ( in_array( $file, $this->force_include[ $file_type ] ) )
  511. )
  512. {
  513. //dont use excluded files like admin bar or already used files
  514. if( in_array( $file, $this->exclude_files[ $file_type ] ) )
  515. {
  516. continue;
  517. }
  518.  
  519. //dont use print stylesheets
  520. if( $enqueued->registered[ $file ]->args == 'print' )
  521. {
  522. continue;
  523. }
  524.  
  525. //if a group condition is set check if the file matches
  526. if( isset( $conditions['groups'] ) && $enqueued->groups[ $file ] != $conditions['groups'] && ! $force_print )
  527. {
  528. continue;
  529. }
  530.  
  531. //the file string we need to generate the final hash
  532. if( ! $force_print )
  533. {
  534. $data['hash'] .= $file;
  535. }
  536.  
  537. //set up correct path
  538. //all the files we need to remove from the wordpress queue once we verified that a compressed version is available
  539. $key = $file . '-' . $file_type;
  540. $data['remove'][ $key ] = array(
  541. 'name' => $file,
  542. 'url' => $enqueued->registered[ $file ]->src,
  543. 'path' => $this->set_path( $enqueued->registered[ $file ]->src ),
  544. 'print'=> $force_print,
  545. 'type' => $file_type,
  546. 'file_content' => '' //only gets generated on new asset file creation or when a file is not stored in the db and required
  547. );
  548.  
  549. /**
  550. * Allow plugins to alter $data parameters (e.g. like path)
  551. *
  552. * @since 4.5.6
  553. * @param array $data
  554. * @param int $enqueued_index $enqueued->to_do index
  555. * @param string $file_type 'js' | 'css'
  556. * @param string $file_group_name 'avia-head-scripts' | 'avia-footer-scripts' | 'avia-merged-styles'
  557. * @param WP_Scripts $enqueued
  558. * @param array $conditions
  559. * @return array
  560. */
  561. $data = apply_filters( 'avf_asset_mgr_get_file_data', $data, $enqueued_index, $file_type, $file_group_name, $enqueued, $conditions );
  562.  
  563.  
  564. //check if the file already exists in our database of stored files. if not add it for future re-use
  565. if( ! isset( $stored_assets[ $key ] ) || $this->testmode )
  566. {
  567. $db_update = true;
  568. $data['remove'][ $key ]['file_content'] = $this->get_file_content( $data['remove'][$key]['path'], $file_type, $data['remove'][ $key ]['url'], $file );
  569. $stored_assets[ $key ] = $data['remove'][ $key ];
  570. }
  571.  
  572. //activate to test if we print all assets to body
  573. //$this->additional_print_to_head[$file_type][] = $file;
  574. }
  575. }
  576.  
  577.  
  578. if( isset( $db_update ) )
  579. {
  580. $this->update_option_fix_cache( $this->db_prefix . $file_type . '_filecontent' , $stored_assets );
  581. }
  582.  
  583. //clean up the todo list
  584. $enqueued->to_do = array();
  585.  
  586. //generate a unique hash based on file name string and version number
  587. $data['hash'] = $file_group_name . '-' . md5( $data['hash'] . $data['version'] );
  588.  
  589. return $data;
  590. }
  591.  
  592. /**
  593. * Return the path to the directory where compressed files are stored excluding / at end
  594. *
  595. * @since 4.2.6
  596. * @added_by Günter
  597. * @return string
  598. */
  599. protected function get_dyn_stylesheet_dir_path()
  600. {
  601. global $avia_config;
  602.  
  603. $wp_upload_dir = wp_upload_dir();
  604. $stylesheet_dir = $wp_upload_dir['basedir'] . $avia_config['dynamic_files_upload_folder'];
  605. $stylesheet_dir = str_replace( '\\', '/', $stylesheet_dir );
  606.  
  607. /**
  608. * @since 4.2.6
  609. * @param string $stylesheet_dir
  610. * @return string
  611. */
  612. $stylesheet_dir = apply_filters( 'avia_dyn_stylesheet_dir_path', $stylesheet_dir );
  613.  
  614. return $stylesheet_dir;
  615. }
  616.  
  617. /**
  618. * Retrieve the content of a css or js file, compress it and return it.
  619. * Since .min.* files are included content of these files is used and no more compression
  620. *
  621. * @since 4.2.4
  622. * @since 5.2 .min.* file content is used by default
  623. * @since 5.3 removed using .min.* files as this breaks relative urls to background images e.g. in woocommerce-mod.css
  624. * @param string $path
  625. * @param string $file_type
  626. * @param string $fallback_url
  627. * @param string $enqueue_id
  628. * @return string
  629. */
  630. protected function get_file_content( $path, $file_type, $fallback_url = '', $enqueue_id = '' )
  631. {
  632. $original_path = $path;
  633. $new_content = false;
  634.  
  635. // if we got a min file rendered we do not touch that
  636. $is_min_file = strpos( $path, ".min.{$file_type}" );
  637.  
  638. /**
  639. * @since 5.3 removed again
  640. * ================================================================================================
  641. */
  642.  
  643. //
  644. // if( false === $is_min_file )
  645. // {
  646. // /**
  647. // * Filter to use our old compression and not an included .min file
  648. // * (might be useful if problems occur with the included files)
  649. // *
  650. // * @since 5.2
  651. // * @param boolean $ignore_min_file
  652. // * @param string $path
  653. // * @param string $file_type
  654. // * @param string $fallback_url
  655. // * @param string $enqueue_id
  656. // * @return boolean true to ignore and use our old compression
  657. // */
  658. // $ignore_min_file = apply_filters( 'avf_ignore_min_file_for_merge', false, $path , $file_type , $fallback_url, $enqueue_id );
  659. //
  660. // // we only take files that have an id of avia
  661. // if( false === $ignore_min_file && ( false !== strpos( $enqueue_id, 'avia-' ) ) )
  662. // {
  663. // $is_min_file = true;
  664. //
  665. // switch( $file_type )
  666. // {
  667. // case 'css':
  668. // $path = preg_replace("((.*).css$)", "$1.min.css", $path );
  669. // break;
  670. // case 'js':
  671. // $path = preg_replace("((.*).js$)", "$1.min.js", $path );
  672. // break;
  673. // default:
  674. // $is_min_file = false;
  675. // break;
  676. // }
  677. // }
  678. // }
  679.  
  680. /**
  681. * =============================================================================================
  682. * End removed again
  683. */
  684.  
  685. //try to retrieve the data by accessing the server
  686. if( ! empty( $path ) )
  687. {
  688. /**
  689. * @used_by currently unused
  690. * @since 4.5.6
  691. * @return string
  692. */
  693. $check_path = trailingslashit( ABSPATH ) . $path;
  694. $check_path = apply_filters( 'avf_compress_file_content_path', $check_path, $path , $file_type , $fallback_url );
  695.  
  696. // avoid throwing E_WARNING if .min file does not exist
  697. $new_content = @file_get_contents( $check_path );
  698.  
  699. // try to read the non minified file
  700. if( is_bool( $new_content ) && $new_content === false && $is_min_file )
  701. {
  702. $not_min_path = str_replace( '.min.', '.', $check_path );
  703. $new_content = @file_get_contents( $not_min_path );
  704.  
  705. // succeeded to read unminified content
  706. if( ! ( is_bool( $new_content ) && $new_content === false ) )
  707. {
  708. $is_min_file = false;
  709. }
  710. }
  711. }
  712.  
  713. //we got a file that we cannot read, lets try to access it via remote get
  714. if( is_bool( $new_content ) && $new_content === false )
  715. {
  716. if( empty( $fallback_url ) )
  717. {
  718. return '';
  719. }
  720.  
  721. /**
  722. * @used_by currently unused
  723. * @since 4.5.6
  724. * @return string
  725. */
  726. $check_fallback_url = apply_filters( 'avf_compress_file_content_fallback_url', $fallback_url, $path , $file_type );
  727.  
  728. $args = array();
  729. if( 'disable_ssl' == avia_get_option( 'merge_disable_ssl' ) )
  730. {
  731. $args['sslverify'] = false;
  732. }
  733.  
  734. // set flag depending on url - we do not try to modify as this is a fallback situation only
  735. $is_min_file = strpos( $check_fallback_url, ".min.{$file_type}" );
  736.  
  737. $response = wp_remote_get( esc_url_raw( $check_fallback_url ), $args );
  738.  
  739. if( ! is_wp_error( $response ) && ( $response['response']['code'] === 200 ) )
  740. {
  741. $new_content = wp_remote_retrieve_body( $response );
  742. }
  743. }
  744.  
  745. //if we still did not retrieve the proper content we dont need to compress the output
  746. if( $new_content !== false )
  747. {
  748. if( ! $is_min_file )
  749. {
  750. $new_content = $this->compress_content( $new_content , $file_type, $path );
  751. }
  752. }
  753. else
  754. {
  755. $new_content = '';
  756. }
  757.  
  758. return $new_content;
  759. }
  760.  
  761. /**
  762. * Generates the merged and compressed file
  763. *
  764. * @since 4.2.4
  765. * @param string $file_type
  766. * @param array $data
  767. * @param WP_Scripts $enqueued
  768. * @return boolean
  769. */
  770. protected function generate_file( $file_type, $data, $enqueued )
  771. {
  772. $file_created = false;
  773.  
  774. //try to create a new folder if necessary
  775. $stylesheet_dir = $this->get_dyn_stylesheet_dir_path();
  776. $isdir = avia_backend_create_folder( $stylesheet_dir );
  777.  
  778. //check if we got a folder (either created one or there already was one). if we got one proceed
  779. if( ! $isdir )
  780. {
  781. return false;
  782. }
  783.  
  784. $content = '';
  785.  
  786. //iterate over existing styles and save the content so we can add it to the compressed file
  787. if( is_array( $data['remove'] ) )
  788. {
  789. $stored_assets = get_option( $this->db_prefix . $file_type . '_filecontent' );
  790.  
  791. foreach( $data['remove'] as $key => $remove )
  792. {
  793. if( $remove['path'] != '' )
  794. {
  795. if( ! $remove['print'] )
  796. {
  797. $content .= $stored_assets[ $key ]['file_content'];
  798. }
  799. }
  800. }
  801. }
  802.  
  803. //create a new file if we got any content
  804. if( trim( $content ) != '' )
  805. {
  806. $file_path = trailingslashit( $stylesheet_dir ) . $data['hash'] . '.' . $file_type;
  807. $file_created = avia_backend_create_file( $file_path, $content );
  808.  
  809. //double check if the file can be accessed
  810. if( is_readable( $file_path ) )
  811. {
  812. $handle = fopen( $file_path, 'r' );
  813. $filecontent = fread( $handle, filesize( $file_path ) );
  814. fclose( $handle );
  815.  
  816. $file = $this->get_file_url( $data, $file_type );
  817.  
  818. $args = array();
  819. if( 'disable_ssl' == avia_get_option( 'merge_disable_ssl' ) )
  820. {
  821. $args['sslverify'] = false;
  822. }
  823.  
  824. $request = wp_remote_get( $file, $args );
  825.  
  826. $file_created = false;
  827. if( ( ! $request instanceof WP_Error ) && is_array( $request ) && isset( $request['body'] ) )
  828. {
  829. $request['body'] = trim($request['body']);
  830. $filecontent = trim( $filecontent );
  831.  
  832. //if the content does not match the file is not accessible
  833. if( $filecontent == $request['body'] )
  834. {
  835. $file_created = true;
  836. }
  837. }
  838. }
  839. }
  840.  
  841. //file creation failed
  842. if( ! $file_created )
  843. {
  844. return false;
  845. }
  846.  
  847. //file creation succeeded, store the url of the file
  848. $generated_files = get_option( $this->db_prefix . $data['file_group_name'] );
  849. if( ! is_array( $generated_files ) )
  850. {
  851. $generated_files = array();
  852. }
  853.  
  854. $generated_files[ $data['hash'] ] = true;
  855.  
  856. $this->update_option_fix_cache( $this->db_prefix . $data['file_group_name'], $generated_files );
  857.  
  858. //if everything worked out return the new file hash, otherwise return false
  859. return true;
  860. }
  861.  
  862. /**
  863. * removes whitespace and comments, fixes relative urls etc
  864. *
  865. * @since 4.2.4
  866. * @param string $content
  867. * @param string $file_type
  868. * @param string $path
  869. * @return string
  870. */
  871. protected function compress_content( $content, $file_type, $path )
  872. {
  873. if( 'css' == $file_type )
  874. {
  875. $content = $this->rel_to_abs_url( $content, $path );
  876.  
  877. if( $this->compress_files[ $file_type ] )
  878. {
  879. $content = aviaAssetManager::css_strip_whitespace( $content );
  880. }
  881. }
  882. else
  883. {
  884. if( $this->compress_files[ $file_type ] )
  885. {
  886. if( version_compare( phpversion(), '5.3', '>=' ) )
  887. {
  888. include_once 'external/JSqueeze.php';
  889.  
  890. $jz = new JSqueeze();
  891.  
  892. $content = $jz->squeeze(
  893. $content,
  894. true, // $singleLine
  895. false, // $keepImportantComments
  896. false // $specialVarRx
  897. );
  898. }
  899. }
  900. }
  901.  
  902. return $content;
  903. }
  904.  
  905. #################
  906. /**
  907. * Switch relative urls in the stylesheet to absolute urls
  908. * For relative paths: https://css-tricks.com/quick-reminder-about-file-paths/
  909. * Full paths: are returned unchanged
  910. * start with // or http:// or https://
  911. *
  912. * @since 4.2.4 modified 4.5.5
  913. * @param string $content
  914. * @param string $path
  915. * @return string
  916. */
  917. protected function rel_to_abs_url( $content, $path )
  918. {
  919. /**
  920. * e.g. WPML needs relative paths when using directories for languages
  921. *
  922. * @since 4.8.8
  923. */
  924. if( current_theme_supports( 'custom_uploaded_fonts_relative_url' ) )
  925. {
  926. return $content;
  927. }
  928.  
  929. // test drive for the regexp : https://regexr.com/3kq8q
  930. // @since 4.5.5 supports UNICODE characters &#8216; - &#8221;
  931.  
  932. $this->base_url = trailingslashit(dirname( get_site_url( null, $path ) ) );
  933. $reg_exUrl = '/url\s*?\([\"|\'|\s|\/|\x{2018}|\x{2019}|\x{201C}|\x{201D}]*([^\:]+?)[\"|\'|\s|\x{2018}|\x{2019}|\x{201C}|\x{201D}]*\)/imu';
  934.  
  935. $content = preg_replace_callback( $reg_exUrl, array( $this, '_url_callback' ), $content );
  936.  
  937. return $content;
  938. }
  939.  
  940.  
  941. /**
  942. * callback function. todo once wp switches to 5.3: make it anonymous again
  943. * remove ../../ from urls and iterate into higher folder from the baseurl
  944. *
  945. * $match[0]: url( path_to_file )
  946. * $match[1]: path_to_file, { ", ', &#8216; - &#8221; } removed and trailing / or \ removed
  947. *
  948. * @since 4.2.4 modified 4.5.5, 4.5.6
  949. * @param array $match
  950. * @return string
  951. */
  952. public function _url_callback( $match )
  953. {
  954. /**
  955. * Check if we have already an absolute url
  956. * (localhost is a special url - starts with //localhost
  957. */
  958. if( ( false !== stripos( $match[1], 'http://' ) ) || ( false !== stripos( $match[1], 'https://' ) ) )
  959. {
  960. return $match[0];
  961. }
  962.  
  963. $base_url = str_replace( array( 'http:', 'https:' ), '', get_home_url() );
  964.  
  965. /**
  966. * Check if user enters URL to the root directory (starts with / or \ for windows systems)
  967. * or an absolute URL
  968. *
  969. * e.g. /wp-content/themes/enfold/imgages/xxx.jpg
  970. */
  971. $start = strpos( $match[0], $match[1] );
  972. if( false === $start )
  973. {
  974. return $match[0];
  975. }
  976.  
  977. $absolute = substr( $match[0], $start - 2, 2 );
  978. if( in_array( $absolute, array( '//', '\\\\' ) ) )
  979. {
  980. /**
  981. * Cross server references currently returned with protocol-relative URL because it had been removed in avia_style_generator::create_styles
  982. */
  983. if( false !== stripos( $match[0], $base_url ) )
  984. {
  985. $match[0] = str_ireplace( $base_url, get_home_url(), $match[0] );
  986. }
  987. else
  988. {
  989. $match[0] = "url('//" . trim( $match[1] ) . "')";
  990. }
  991.  
  992. return $match[0];
  993. }
  994.  
  995. $root = substr( $match[0], $start - 1, 1 );
  996. if( in_array( $root, array( '/', '\\' ) ) )
  997. {
  998. return "url('" . trailingslashit( get_home_url() ) . trim( $match[1] ) . "')";
  999. }
  1000.  
  1001. $current_base = $this->base_url;
  1002. $segments = explode( '../', $match[1] );
  1003. $seg_count = count( $segments ) - 1;
  1004.  
  1005. for( $i = $seg_count; $i > 0; $i-- )
  1006. {
  1007. $current_base = dirname( $current_base );
  1008. }
  1009.  
  1010. $new_url = trailingslashit( $current_base ) . end( $segments );
  1011.  
  1012. return "url('{$new_url}')";
  1013. }
  1014.  
  1015. #################
  1016.  
  1017.  
  1018. /**
  1019. * Minify css content
  1020. * ( also copied to enfold\framework\php\class-style-generator.php )
  1021. *
  1022. * @since 4.2.4
  1023. * @param string $css
  1024. * @return string
  1025. */
  1026. static public function css_strip_whitespace( $css )
  1027. {
  1028. $replace = array(
  1029. "#/\*.*?\*/#s" => '', // Strip C style comments.
  1030. "#\s\s+#" => ' ', // Strip excess whitespace.
  1031. "#\t#" => ''
  1032. );
  1033.  
  1034. $search = array_keys( $replace );
  1035. $css = preg_replace( $search, $replace, $css );
  1036.  
  1037. $replace = array(
  1038. ': ' => ':',
  1039. '; ' => ';',
  1040. ' {' => '{',
  1041. ' }' => '}',
  1042. ', ' => ',',
  1043. '{ ' => '{',
  1044. '{ ' => '{',
  1045. ";\n" => ';', // Put all rules from one selector into one line
  1046. ';}' => '}', // Strip optional semicolons.
  1047. ",\n" => ',', // Don't wrap multiple selectors.
  1048. "\n}" => '}', // Don't wrap closing braces.
  1049. "{\n" => '{', // Don't wrap the first rule of a selector.
  1050. //"} " => "}\n", // Put each rule on it's own line.
  1051. "\n" => '', //replace all newlines
  1052. );
  1053.  
  1054. $search = array_keys( $replace );
  1055. $css = str_replace( $search, $replace, $css );
  1056.  
  1057. return trim( $css );
  1058. }
  1059.  
  1060. /**
  1061. * remove all db keys starting with the $this->db_prefix - this way all files will be generated new on next pageload
  1062. * clean up the generated files in the folder
  1063. *
  1064. * @since 4.2.4
  1065. */
  1066. public function reset_db_asset_list()
  1067. {
  1068. global $wpdb;
  1069.  
  1070. $results = $wpdb->get_results( "SELECT * FROM {$wpdb->prefix}options WHERE option_name LIKE '{$this->db_prefix}%'", OBJECT );
  1071. $stylesheet_dir = $this->get_dyn_stylesheet_dir_path();
  1072.  
  1073. foreach( $results as $result )
  1074. {
  1075. //delete db option
  1076. $this->delete_option_fix_cache( $result->option_name );
  1077.  
  1078. /**
  1079. * remove files. by default they are NOT deleted to not cause any issues with caching plugins.
  1080. * an option in the backend allows to change that setting
  1081. */
  1082. $file_group_name = str_replace( $this->db_prefix, '', $result->option_name );
  1083.  
  1084. /**
  1085. *
  1086. * @used_by enfold\includes\helper-assets.php av_delete_asset_switch() 10
  1087. * @since 4.2.4
  1088. * @param boolean $delete
  1089. * @return boolean
  1090. */
  1091. $delete_files = apply_filters( 'avf_delete_assets', false );
  1092.  
  1093. //fallback: if the folder gets to large we will clean up older files nevertheless
  1094. $files_to_delete = array();
  1095.  
  1096. foreach( glob( $stylesheet_dir . '/' . $file_group_name . '*' ) as $file )
  1097. {
  1098. $files_to_delete[ filemtime( $file ) ] = $file;
  1099. }
  1100.  
  1101. //set the number of files to remove. if delete files is enabled remove all, otherwise only the oldest 5
  1102. $remove_files = 0;
  1103. if( count( $files_to_delete ) > 100 )
  1104. {
  1105. $remove_files = 5;
  1106. }
  1107.  
  1108. if( $delete_files )
  1109. {
  1110. $remove_files = count( $files_to_delete );
  1111. }
  1112.  
  1113. foreach( $files_to_delete as $creation => $file )
  1114. {
  1115. if( $remove_files > 0 )
  1116. {
  1117. unlink( $file );
  1118. $remove_files --;
  1119. }
  1120. }
  1121. }
  1122. }
  1123.  
  1124. /**
  1125. * dequeue and deregister scripts
  1126. * update: instead of removing the scripts just remove the src attribute. this way the file does not get loaded but dependencies stay intact
  1127. *
  1128. * @since 4.2.4
  1129. * @param string|false $attach_data_to
  1130. */
  1131. protected function try_deregister_scripts( $attach_data_to = false )
  1132. {
  1133. global $wp_styles, $wp_scripts;
  1134.  
  1135. foreach( $this->deregister_files as $file_type => $files )
  1136. {
  1137. //get the data of the file we would generate
  1138. $enqueued = ( $file_type == 'css' ) ? $wp_styles : $wp_scripts;
  1139.  
  1140. foreach( $files as $remove )
  1141. {
  1142. $enqueued->registered[ $remove ]->src = '';
  1143.  
  1144. //the extra data needs to be attached to the compressed file in order to be printed properly
  1145. if( $attach_data_to && isset( $enqueued->registered[ $remove ]->extra ) )
  1146. {
  1147. //first copy the data attribute to our enqueued file
  1148. if( isset( $enqueued->registered[ $remove ]->extra['data'] ) )
  1149. {
  1150. if( ! isset( $enqueued->registered[ $attach_data_to ]->extra['data'] ) )
  1151. {
  1152. $enqueued->registered[ $attach_data_to ]->extra['data'] = '';
  1153. }
  1154.  
  1155. $enqueued->registered[ $attach_data_to ]->extra['data'] .= $enqueued->registered[ $remove ]->extra['data'];
  1156. // reset data $enqueued->registered[$remove]->extra['data'] = '';
  1157. }
  1158.  
  1159. //now merge the before and after arrays
  1160. $before_after = array( 'before', 'after' );
  1161.  
  1162. foreach( $before_after as $state )
  1163. {
  1164. if( isset( $enqueued->registered[ $remove ]->extra[ $state ] ) )
  1165. {
  1166. /**
  1167. * Fix a possible bug occurring on some installs - occur ocasionally - reporting "Creating default object from empty value"
  1168. * https://kriesi.at/support/topic/clean-enfold-install-with-woocommerce-and-demo-data-results-in-critical-error/
  1169. * https://github.com/KriesiMedia/wp-themes/issues/3281
  1170. *
  1171. * Still occurring - added a check for $enqueued->registered[ $attach_data_to ]
  1172. * https://github.com/KriesiMedia/wp-themes/issues/4051
  1173. *
  1174. * @since 4.8.4
  1175. */
  1176. if( ! isset( $enqueued->registered[ $attach_data_to ]->extra ) )
  1177. {
  1178. if( ! isset( $enqueued->registered[ $attach_data_to ] ) )
  1179. {
  1180. $enqueued->registered[ $attach_data_to ] = new stdClass();
  1181. }
  1182.  
  1183. $enqueued->registered[ $attach_data_to ]->extra = array();
  1184. }
  1185.  
  1186. if( ! isset( $enqueued->registered[ $attach_data_to ]->extra[ $state ] ) )
  1187. {
  1188. $enqueued->registered[ $attach_data_to ]->extra[ $state ] = array();
  1189. }
  1190.  
  1191.  
  1192. $a = $enqueued->registered[ $attach_data_to ]->extra[ $state ];
  1193. $b = $enqueued->registered[ $remove ]->extra[ $state ];
  1194.  
  1195. $enqueued->registered[ $attach_data_to ]->extra[ $state ] = array_merge( $a, $b );
  1196. }
  1197. }
  1198. }
  1199. }
  1200.  
  1201. /* deprecated
  1202. foreach($files as $remove)
  1203. {
  1204. if($file_type == 'css')
  1205. {
  1206. wp_dequeue_style( $remove );
  1207. wp_deregister_style( $remove );
  1208. }
  1209. else
  1210. {
  1211. wp_dequeue_script( $remove );
  1212. wp_deregister_script( $remove );
  1213. }
  1214. }*/
  1215. }
  1216.  
  1217. }
  1218.  
  1219. /**
  1220. *
  1221. * @since 4.2.4
  1222. * @param string $registered_path
  1223. * @return string
  1224. */
  1225. protected function set_path( $registered_path )
  1226. {
  1227. $path = str_replace( site_url(), '', $registered_path );
  1228.  
  1229. if( strpos( $path, '//' ) === 0 ) //if the path starts with // - eg: used by plugins like woocommerce
  1230. {
  1231. $remove = explode( '//', site_url() );
  1232. $path = str_replace( '//' . $remove[1], '', $registered_path );
  1233. }
  1234.  
  1235. $path = ltrim( $path, '/' );
  1236.  
  1237. return $path;
  1238. }
  1239.  
  1240. /**
  1241. * Handler to print inline
  1242. *
  1243. * @since 4.2.4
  1244. */
  1245. public function inline_print_assets()
  1246. {
  1247. global $wp_styles, $wp_scripts;
  1248.  
  1249. $assets_to_print = array_merge_recursive( $this->force_print_to_head, $this->additional_print_to_head );
  1250. $output = '';
  1251.  
  1252. foreach( $assets_to_print as $file_type => $assets )
  1253. {
  1254. // skip if no assets are set to be printed
  1255. if( empty( $assets ) )
  1256. {
  1257. continue;
  1258. }
  1259.  
  1260. $stored_assets = get_option( $this->db_prefix . $file_type . '_filecontent' );
  1261. if( false === $stored_assets || ! is_array( $stored_assets ) || empty( $stored_assets ) )
  1262. {
  1263. continue;
  1264. }
  1265.  
  1266. $enqueued = ( $file_type == 'css' ) ? $wp_styles : $wp_scripts;
  1267. $print = '';
  1268.  
  1269. foreach( $assets as $asset )
  1270. {
  1271. //skip if the file is not enqueued
  1272. if( ! in_array( $asset, $enqueued->queue ) )
  1273. {
  1274. continue;
  1275. }
  1276.  
  1277. $print .= $stored_assets[ $asset . '-' . $file_type ]['file_content'];
  1278. }
  1279.  
  1280. if( ! empty( $print ) && $file_type == 'css' )
  1281. {
  1282. $output.= '<style type="text/css" media="screen">' . $print . '</style>';
  1283. }
  1284.  
  1285. if( ! empty( $print ) && $file_type == 'js' )
  1286. {
  1287. $output.= '<script type="text/javascript">' . $print . '</script>';
  1288. }
  1289.  
  1290. }
  1291.  
  1292. if( ! empty( $output ) )
  1293. {
  1294. $output = "\n<!-- To speed up the rendering and to display the site as fast as possible to the user we include some styles and scripts for above the fold content inline -->\n" . $output;
  1295. echo $output;
  1296. }
  1297.  
  1298. }
  1299.  
  1300. /**
  1301. * Update option and provide a fix for caching plugins
  1302. * see https://github.com/pantheon-systems/wp-redis/issues/221
  1303. *
  1304. * @since 4.7.5.1
  1305. * @param string $option
  1306. * @param mixed $value
  1307. * @param string|bool $autoload
  1308. * @return bool
  1309. */
  1310. protected function update_option_fix_cache( $option, $value, $autoload = null )
  1311. {
  1312. $return = update_option( $option, $value, $autoload );
  1313. $this->fix_all_options_cache( $option, true );
  1314.  
  1315. return $return;
  1316. }
  1317.  
  1318. /**
  1319. * Delete option and provide a fix for caching plugins
  1320. * see https://github.com/pantheon-systems/wp-redis/issues/221
  1321. *
  1322. * @since 4.7.5.1
  1323. * @param string $option
  1324. * @return bool
  1325. */
  1326. protected function delete_option_fix_cache( $option )
  1327. {
  1328. $return = delete_option( $option );
  1329. $this->fix_all_options_cache( $option );
  1330.  
  1331. return $return;
  1332. }
  1333.  
  1334. /**
  1335. * Fix racing condition in options table
  1336. *
  1337. * see https://github.com/pantheon-systems/wp-redis/issues/221
  1338. * see https://core.trac.wordpress.org/ticket/31245#comment:57
  1339. *
  1340. * @since 4.7.5.1
  1341. * @param string $option
  1342. * @param boolean $force_reload
  1343. */
  1344. protected function fix_all_options_cache( $option, $force_reload = false )
  1345. {
  1346. if( false === strpos( avia_get_option( 'merge_disable_unique_timestamp' ), 'fix_wp_bug' ) )
  1347. {
  1348. return;
  1349. }
  1350.  
  1351. avia_backend_fix_all_options_cache( $option, $force_reload );
  1352. }
  1353.  
  1354.  
  1355. } // end class
  1356.  
  1357. } // end if !class_exists
Advertisement
Add Comment
Please, Sign In to add comment