HEX
Server: Apache
System: Linux webd006.cluster128.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
User: zwafgyp (177615)
PHP: 7.4.33
Disabled: _dyuweyrj4,_dyuweyrj4r,dl
Upload Files
File: /home/zwafgyp/lacalita/wp-content/plugins/rey-core/inc/assets-handler.php
<?php
namespace ReyCore;

use ReyCore\Helper;

if (!defined('ABSPATH')) exit; // Exit if accessed directly

class AssetsHandler
{
	/**
	 * Store Filesystem
	 */
	private static $fs;

	/**
	 * Path where to save files.
	 */
	private static $dir_path;

	/**
	 * WP Uploads Folder
	 *
	 * @var array
	 */
	private static $wp_uploads_dir = [];

	/**
	 * Assets manager
	 *
	 * @var AssetsManager
	 */
	public static $assets_manager;

	/**
	 * Holds registered styles
	 *
	 * @var array
	 */
	private static $registered_styles = [];

	/**
	 * Holds registered scripts
	 *
	 * @var array
	 */
	private static $registered_scripts = [];

	/**
	 * Holds collected scripts
	 *
	 * @var array
	 */
	private $scripts = [];

	/**
	 * Buffer manager instance
	 *
	 * @var BufferManager
	 */
	private static $buffer;

	/**
	 * Handle log times
	 *
	 * @var string
	 */
	private static $current_time;

	/**
	 * Determine what to log
	 *
	 * @var array
	 */
	public static $logs = [
		'time'          => false,
		'styles'        => false,
		'scripts'       => false,
		'print_handles' => true,
	];

	/**
	 * Detects paths in CSS.
	 */
	const ASSETS_REGEX = '/url\s*\(\s*(?!["\']?data:)(?![\'|\"]?[\#|\%|])([^)]+)\s*\)([^;},\s]*)/i';

	public function __construct($manager)
	{

		self::set_filesystem();

		self::$assets_manager = $manager;

		add_action( 'reycore/buffer/assets', [$this, 'handle_assets']);
		add_action( 'init', [$this, 'handle_clear_data']);
		add_action( 'rey/flush_cache_after_updates', [$this, 'clear__basic'], 20);
		add_action( 'reycore/refresh_all_caches', [$this, 'clear__basic'], 10);
		add_filter( 'reycore/admin_bar_menu/nodes', [$this, 'adminbar__add_refresh'], 20);
		add_action( 'wp_ajax__refresh_assets', [$this, 'adminbar__clear_assets']);

	}

	public function handle_assets($buffer)
	{
		self::$buffer = $buffer;

		if( self::$assets_manager->get_settings('disable_assets') ){
			return;
		}

		$this->handle_css();
		$this->handle_js();

	}

	/**
	 * Start finding CSS occurances and handle CSS merging
	 *
	 * @return void
	 */
	public function handle_css(){

		if( ! self::$assets_manager->get_settings('save_css') ){
			return;
		}

		// start timing and logging
		do_action( 'qm/start', 'rey_handle_css' );

		// define excludes from merging
		$excludes = apply_filters('reycore/buffer/css/excluded', [
			REY_CORE_STYLESHEET_HANDLE,
			'reycore-frontend-admin',
		]);

		$output = '';

		self::$registered_styles = self::$assets_manager->get_register_assets('styles');

		foreach (self::$assets_manager->get_styles() as $type => $stylesheets) {

			if( empty($stylesheets) ){
				continue;
			}

			// usually dev. mode
			if( ! self::$assets_manager->get_settings('css_combine') ){
				foreach ($stylesheets as $stylesheet_handle) {
					if( isset(self::$registered_styles[$stylesheet_handle]) && ($stylesheet_data = self::$registered_styles[$stylesheet_handle]) ){
						$output .= self::print_css_tag($type, $stylesheet_data, $stylesheet_handle );
					}
				}
				continue;
			}

			$stylesheets = array_diff($stylesheets, $excludes);

			$hash = self::get_hash( $stylesheets, self::$registered_styles );

			$file = [
				'path' => self::$dir_path . self::get__stylesheet__basename($hash, $type), // server
				'src' => self::get__base_uploads__url() . self::get__stylesheet__basename($hash, $type), // url
			];

			$css_content = '';

			// if it already exists, don't rewrite it, just stop
			if( ! self::$fs->is_file( $file['path'] ) ){
				// write the CSS file
				$css_content = $this->write_css($stylesheets, $type, $file['path']);
			}

			if( self::$assets_manager::NAME_HEAD === $type && 'inline' === self::$assets_manager->get_settings('css_head') ){
				$output .= self::print_css_tag_inline($css_content, $file);
			}
			else {
				$output .= self::print_css_tag($type, $file);
			}

		}

		// update the buffer
		$this->update_buffer( self::$assets_manager::HEAD_PLACEHOLDER, $output );

		do_action( 'qm/stop', 'rey_handle_css' );

	}

	/**
	 * Write stylesheet to the uploads folder.
	 *
	 * @param array $stylesheets
	 * @param string $type
	 * @param string $filepath Target stylesheet
	 * @return void
	 */
	private function write_css( $stylesheets, $type, $filepath){

		$css = [];
		$data_to_log = [];

		// go through stylesheets
		foreach ($stylesheets as $handle ) {

			$stylesheet_data = self::$registered_styles[$handle];

			// check for path and if the file actually exists
			if( ! (isset($stylesheet_data['path']) && self::$fs->is_file( $stylesheet_data['path'] )) ) {
				continue;
			}

			// grabs CSS
			$stylesheet_css = self::$fs->get_contents( $stylesheet_data['path'] );
			// fix urls
			$stylesheet_css = self::fixurls($stylesheet_data['path'], $stylesheet_css);
			// collect css
			$css[$handle] = $stylesheet_css;
			// collect logging handles
			$data_to_log[] = $handle;
		}

		// check if CSS data exists
		if( ! empty($css) ){

			// minify the css data
			$css_contents = self::minify_css($css);

			// log
			self::log( sprintf('Stored %2$s stylesheet in "%3$s", FN: "%1$s".', $filepath, strtoupper($type), reycore__get_page_title() ) );

			// append handles
			$css_contents .= $this->debug_print_handles($data_to_log);

			// actually write the file
			self::$fs->put_contents( $filepath, $css_contents );

			return $css_contents;
		}

		return false;
	}

	/**
	 * Start finding JS occurances and handle JS merging
	 *
	 * @return void
	 */
	public function handle_js(){

		if( ! self::$assets_manager->get_settings('save_js') ){
			return;
		}

		do_action( 'qm/start', 'rey_handle_js' );

		$scripts = self::$assets_manager->get_all_scripts();

		if( ! empty( $scripts ) ){

			// define excludes from merging
			$scripts = array_diff($scripts, apply_filters('reycore/buffer/js/excluded', [
				'reyadminbar',
				'reycore-frontend-admin',
			]));

			self::$registered_scripts = self::$assets_manager->get_register_assets('scripts');

			// create unique hash
			$hash = self::get_hash( $scripts, self::$registered_scripts );

			$file = [
				'path' => self::$dir_path . self::get__scripts__basename($hash), // server
				'url' => self::get__base_uploads__url() . self::get__scripts__basename($hash), // url
			];

			// if it already exists, don't rewrite it, just stop
			if( ! self::$fs->is_file( $file['path'] ) ){
				// write the JS file
				$this->write_js($scripts, $file['path']);
			}

			$search_for = '</body>';

			// update the buffer
			$this->update_buffer(
				$search_for,
				$this->get_script_output( $file['url'], self::get_version($file)) . $search_for
			);

		}

		do_action( 'qm/stop', 'rey_handle_js' );

	}

	public function get_script_output( $path, $version ){

		// delayed
		if( self::$assets_manager->get_settings('delay_js') ){

			// delay JS
			// reyDelay-DOMContentLoaded to be added to delay_forced_js_event
			// exclus lazy bg
			// is logged out?. Just current_user_can('administrator') to not delay JS.
			return sprintf("<script type='text/javascript' id='rey-delay-js' %s>
				(function(){
					let didDelayJS = false;
					const events = [ 'mousemove', 'scroll', 'keydown', 'click', 'touchstart' ];
					events.forEach(event => {
						document.body.addEventListener(event, function(){
							if( ! didDelayJS) {
								new Promise(function(resolve, reject) {
									var script = document.createElement('script');
										const url = '%s?%s';
										script.src = url;
										script.id = 'rey-combined-js';
										script.async = false;
										script.onload = () => {
											// dispatch event
											document.dispatchEvent(new CustomEvent('reyDelay-DOMContentLoaded'));
											resolve(url);
										};
										script.onerror = () => {reject(url)};
									document.body.appendChild(script);
									didDelayJS = true;
									console.log(script);
								});
							}
						}, {once: true});
					});
				})();
			</script>", reycore__js_no_opt_attr(), $path, $version );

		}

		return sprintf('<script defer type="text/javascript" id="rey-combined-js" src="%1$s?ver=%2$s"></script>', $path, $version ) . "\n";
	}

	/**
	 * Write scripts to the uploads folder.
	 *
	 * @param string $filepath Target script
	 * @return void
	 */
	private function write_js($scripts, $filepath){

		$js = [];
		$data_to_log = [];


		// go through scripts
		foreach ($scripts as $handle ) {

			if( ! isset(self::$registered_scripts[$handle]) ){
				continue;
			}

			$script = self::$registered_scripts[$handle];

			// check for path and if the file actually exists
			if( ! (isset($script['path']) && self::$fs->is_file( $script['path'] )) ) {
				continue;
			}

			// collect css
			$js[$handle] = self::$fs->get_contents( $script['path'] );

			// collect logging handles
			$data_to_log[] = $handle;
		}

		// check if CSS data exists
		if( ! empty($js) ){

			// log
			self::log( sprintf('Stored SCRIPT in "%2$s", FN: "%1$s".', $filepath, reycore__get_page_title() ) );

			// append handles
			$js[] = $this->debug_print_handles($data_to_log);

			// actually write the file
			return self::$fs->put_contents( $filepath, implode('', $js) );
		}

		return false;
	}

	/**
	 * Update the buffer by replacing the existing tags.
	 *
	 * @param string $tag
	 * @param string $replace_with
	 * @return void
	 */
	private function update_buffer($tag, $replace_with = '', $first_only = false){

		// get existing buffer content
		$buffer_content = self::$buffer->get_buffer();

		if( $first_only ){
			$new_content = preg_replace('/' . $tag . '/', $replace_with, $buffer_content, 1);
		}
		else {
			// remove existing ones
			$new_content = str_replace($tag, $replace_with, $buffer_content);
		}

		// update buffer content
		self::$buffer->set_buffer($new_content);

	}

	/**
	 * Render the CSS tags
	 *
	 * @param string $type
	 * @param array $file
	 * @param string $defer
	 * @return string
	 */
	public static function print_css_tag($type, $file, $id = '' ){

		$handle = sprintf('rey-%s-css', ($id ? $id : $type));

		$tag = [
			'start' => sprintf('<link id="%1$s" type="text/css" href="%2$s?ver=%3$s" %4$s', $handle, $file['src'], self::get_version($file), reycore__css_no_opt_attr() ),
			'mid' => ' rel="stylesheet" media="all" ',
			'end' => ' />' . "\n",
			'noscript' => sprintf('<noscript><link rel="stylesheet" href="%s" data-no-minify="1"></noscript>', $file['src']) . "\n",
		];

		// if head, can load defer or block
		if( self::$assets_manager::NAME_HEAD === $type ){
			// maybe some day when critical is generated
			if( 'defer' === self::$assets_manager->get_settings('css_head') ){
				return $tag['start'] . self::$assets_manager::get_defer_method() . $tag['end'] . $tag['noscript'];
			}
			else {

				$the_tag = $tag['start'] . $tag['mid'] . $tag['end'];

				// main stylesheet in block mode, should be handled by caching plugins
				if( 'block' === self::$assets_manager->get_settings('css_head') ){
					$the_tag = str_replace(reycore__css_no_opt_attr(), '', $the_tag);
					wp_register_style( $handle, $file['src'] ); // register style in WP
				}

				return $the_tag;
			}
		}

		// DF, deferred or interactive
		// deferred early or late
		else if( self::$assets_manager::NAME_LAZY === $type ){

			$delay_mode = self::$assets_manager->get_settings('css_delay_mode');

			if( $delay_mode && in_array($delay_mode, ['defer', 'defer_late'], true) ){
				// block the deferred stylesheet
				if( defined('REY_DEFER_TEST') && ! current_user_can('administrator') ){
					return '';
				}
				wp_register_style( $handle, $file['src'] ); // register style in WP
				return $tag['start'] . self::$assets_manager::get_defer_method( 'defer' === $delay_mode ) . $tag['end'] . $tag['noscript'];
			}

			else if( 'block' === $delay_mode ){
				$the_tag = $tag['start'] . $tag['mid'] . $tag['end'];
				$the_tag = str_replace(reycore__css_no_opt_attr(), '', $the_tag);
				wp_register_style( $handle, $file['src'] ); // register style in WP
				return $the_tag;
			}

			else {
				return str_replace('href', self::$assets_manager::LAZY_ATTRIBUTE, $tag['start']) . $tag['mid'] . $tag['end'];
			}
		}

	}

	/**
	 * Render the CSS tag inline
	 *
	 * @param string $type
	 * @param array $file
	 * @param string $defer
	 * @return string
	 */
	public static function print_css_tag_inline($css_content, $file){

		if( ! $css_content ){
			$css_content = self::$fs->get_contents( $file['path'] );
		}

		return sprintf('<style type="text/css" id="rey-%2$s-css" %3$s>%1$s</style>', $css_content, self::$assets_manager::NAME_HEAD, reycore__css_no_opt_attr() ) . "\n";
	}


	/**
	 * Returns the current version with or without a suffix
	 *
	 * @param array $file
	 * @return string
	 */
	public static function get_version($file){

		$suffix = '';
		if( isset($file['path']) && self::$fs->is_file( $file['path'] )){
			$suffix = '.' . filemtime( $file['path'] );
		}

		return REY_CORE_VERSION . $suffix;
	}

	/**
	 * Very basic and minimal minification of the CSS.
	 * Should be actually done by caching plugins
	 *
	 * @param array $css
	 * @return string
	 */
	public static function minify_css($css = []){

		$css_content = str_replace(
			[
				': ',
				';  ',
				'; ',
				'  '
			],
			[
				':',
				';',
				';',
				' '
			],
			preg_replace( "/\r|\n/", '', implode('', $css) )
		);

		// // comments
		// $string = preg_replace('!/\*.*?\*/!s','', $string);
		// $string = preg_replace('/\n\s*\n/',"\n", $string);

		// // space
		// $string = preg_replace('/[\n\r \t]/',' ', $string);
		// $string = preg_replace('/ +/',' ', $string);
		// $string = preg_replace('/ ?([,:;{}]) ?/','$1',$string);

		// // trailing;
		// $string = preg_replace('/;}/','}',$string);

		return $css_content;
	}

	public static function get_hash($handles, $registered){

		$items = [];

		foreach ($handles as $handle) {
			$version = isset($registered[$handle]['version']) ? $registered[$handle]['version'] : REY_CORE_VERSION;
			$items[] = $handle . '-' . $version;
		}

		return Helper::hash( $items );
	}

	/**
	 * Set the filesystem app and the upload
	 * folders
	 *
	 * @return void
	 */
	public static function set_filesystem(){

		if( self::$fs ){
			return self::$fs;
		}

		if( !($fs = reycore__wp_filesystem()) ){
			return;
		}

		self::$fs = $fs;

		$dir_path = self::get__base_uploads__dir();

		if ( ! self::$fs->is_dir( $dir_path ) ) {
			self::$fs->mkdir( $dir_path );
		}

		self::$dir_path = trailingslashit( $dir_path );

		return self::$fs;
	}

	/**
	 * Get WordPress Uploads folder absolute path
	 *
	 * @return string
	 */
	private static function get__wp_uploads__dir() {
		global $blog_id;

		if ( empty( self::$wp_uploads_dir[ $blog_id ] ) ) {
			self::$wp_uploads_dir[ $blog_id ] = wp_upload_dir( null, false );
		}

		return self::$wp_uploads_dir[ $blog_id ];
	}

	/**
	 * Get Rey's Uploads folder absolute path
	 *
	 * @return string
	 */
	public static function get__base_uploads__dir() {
		$wp_upload_dir = self::get__wp_uploads__dir();
		return trailingslashit($wp_upload_dir['basedir']) . REY_CORE_THEME_NAME . '/';
	}

	/**
	 * Get Rey's Uploads folder relative site path
	 *
	 * @return string
	 */
	public static function get__base_uploads__url() {
		$wp_upload_dir = self::get__wp_uploads__dir();
		return trailingslashit(set_url_scheme( $wp_upload_dir['baseurl'] )) . REY_CORE_THEME_NAME . '/';
	}

	/**
	 * Get the stylesheets file base name
	 *
	 * @param string $hash
	 * @param string $type
	 * @return string
	 */
	private static function get__stylesheet__basename( $hash, $type = '' ){
		return sprintf('%s-%s%s.css', ( ! $type ? self::$assets_manager::NAME_HEAD : $type), $hash, AssetsManager::rtl());
	}

	/**
	 * Get the combined script file base name
	 *
	 * @param string $hash
	 * @return string
	 */
	private static function get__scripts__basename( $hash ){
		return sprintf('scripts-%s.js', $hash);
	}

	/**
	 * Log messages to console if debug enabled
	 *
	 * @param string $message
	 * @return void
	 */
	public static function log($message){
		do_action( 'qm/debug', '::Assets: ' . $message );
		if( self::is_debug_log_assets() ){
			error_log(var_export( '::Assets: ' . $message ,true));
		}
	}

	public static function start_log_time(){}
	public static function end_log_time($prefix = ''){}

	/**
	 * Adds comments inside the files
	 *
	 * @param array $data_to_log
	 * @return string
	 */
	public function debug_print_handles( $data_to_log ){

		if( self::$logs['print_handles'] && self::is_debug_log_assets() ){
			return "\r\n" . '/** ' . "\r\n" . implode( "\r\n", $data_to_log ) . "\r\n" . reycore__get_page_title() . "\r\n" . '*/';
		}

	}

	/**
	 * Checks if logging is enabled
	 *
	 * @return boolean
	 */
	public static function is_debug_log_assets(){
		return defined('REY_DEBUG_LOG_ASSETS') && REY_DEBUG_LOG_ASSETS;
	}

	/**
	 * Retrieve assets paths
	 *
	 * @param array $assets
	 * @param string $type
	 * @return array
	 */
	public function get_assets_paths( $assets, $type = 'styles' ){ return []; }

	/**
	 * Make sure URL's are absolute iso relative to original CSS location.
	 *
	 * @param string $file filename of optimized CSS-file.
	 * @param string $code CSS-code in which to fix URL's.
	 */
	static function fixurls( $file, $code )
	{
		// Switch all imports to the url() syntax.
		$code = preg_replace( '#@import ("|\')(.+?)\.css.*?("|\')#', '@import url("${2}.css")', $code );


		if ( preg_match_all( self::ASSETS_REGEX, $code, $matches ) ) {

			$wp_content_name = '/' . wp_basename( WP_CONTENT_DIR );
			$wp_root_dir = substr( WP_CONTENT_DIR, 0, strlen( WP_CONTENT_DIR ) - strlen( $wp_content_name ) );
			$wp_root_url = str_replace( $wp_content_name, '', content_url() );

			$file = str_replace( $wp_root_dir, '/', $file );
			$dir = dirname( $file ); // Like /themes/expound/css.

			/**
			 * $dir should not contain backslashes, since it's used to replace
			 * urls, but it can contain them when running on Windows because
			 * fixurls() is sometimes called with `ABSPATH . 'index.php'`
			 */
			$dir = str_replace( '\\', '/', $dir );
			unset( $file ); // not used below at all.

			$replace = array();
			foreach ( $matches[1] as $k => $url ) {
				// Remove quotes.
				$url      = trim( $url, " \t\n\r\0\x0B\"'" );
				$no_q_url = trim( $url, "\"'" );
				if ( $url !== $no_q_url ) {
					$removed_quotes = true;
				} else {
					$removed_quotes = false;
				}

				if ( '' === $no_q_url ) {
					continue;
				}

				$url = $no_q_url;
				if ( '/' === $url[0] || preg_match( '#^(https?://|ftp://|data:)#i', $url ) ) {
					// URL is protocol-relative, host-relative or something we don't touch.
					continue;
				} else { // Relative URL.

					$newurl = preg_replace( '/https?:/', '', str_replace( ' ', '%20', $wp_root_url . str_replace( '//', '/', $dir . '/' . $url ) ) );


					/**
					 * Hash the url + whatever was behind potentially for replacement
					 * We must do this, or different css classes referencing the same bg image (but
					 * different parts of it, say, in sprites and such) loose their stuff...
					 */
					$hash = md5( $url . $matches[2][ $k ] );
					$code = str_replace( $matches[0][ $k ], $hash, $code );

					if ( $removed_quotes ) {
						$replace[ $hash ] = "url('" . $newurl . "')" . $matches[2][ $k ];
					} else {
						$replace[ $hash ] = 'url(' . $newurl . ')' . $matches[2][ $k ];
					}
				}
			}

			$code = self::replace_longest_matches_first( $code, $replace );
		}

		return $code;
	}

	/**
	 * Given an array of key/value pairs to replace in $string,
	 * it does so by replacing the longest-matching strings first.
	 *
	 * @param string $string string in which to replace.
	 * @param array  $replacements to be replaced strings and replacement.
	 *
	 * @return string
	 */
	protected static function replace_longest_matches_first( $string, $replacements = array() )
	{
		if ( ! empty( $replacements ) ) {
			// Sort the replacements array by key length in desc order (so that the longest strings are replaced first).
			$keys = array_map( 'strlen', array_keys( $replacements ) );
			array_multisort( $keys, SORT_DESC, $replacements );
			$string = str_replace( array_keys( $replacements ), array_values( $replacements ), $string );
		}

		return $string;
	}

	// CLEAR DATA

	public function adminbar__add_refresh($nodes){

		if( ! current_user_can('administrator') ){
			return $nodes;
		}

		$assets_settings = self::$assets_manager->get_settings();

		if( ! ($assets_settings['save_css'] || $assets_settings['save_js']) ){
			return $nodes;
		}

		if( isset($nodes['refresh']) ){
			$nodes['refresh']['nodes']['refresh_assets'] = [
				'title'  => esc_html__( 'Assets Cache', 'rey-core' ) . ' (CSS/JS)',
				'href'  => '#',
				'meta_title' => esc_html__( 'Refresh the combined and minified assets.', 'rey-core' ),
				'class' => 'qm-refresh-assets qm-refresher',
			];
		}

		return $nodes;
	}

	/**
	 * Refresh Assets through Ajax
	 *
	 * @since 2.0.0
	 **/
	public function adminbar__clear_assets()
	{
		if( $this->clear_assets() ){
			wp_send_json_success();
		}

		wp_send_json_error();
	}

	private function clear_assets(){

		if( ! current_user_can('administrator') ){
			self::log( 'Incorrect permissions for clearing assets!' );
			return;
		}

		if( ! self::$fs ){
			self::log( 'Filesystem missing for clearing assets!' );
			return;
		}

		$assets_settings = self::$assets_manager->get_settings();

		if( ! ($assets_settings['save_css'] || $assets_settings['save_js']) ){
			return;
		}

		$cleared = [];

		if( is_multisite() ){
			foreach ( get_sites() as $blog ) {
				switch_to_blog( $blog->blog_id );

					$dir_path = self::get__base_uploads__dir();

					if ( self::$fs->rmdir( $dir_path, true ) ) {
						self::$fs->mkdir( $dir_path );
						$cleared[] = true;
					}

				restore_current_blog();
			}
		}
		else {
			if ( self::$fs->rmdir( self::$dir_path, true ) ) {
				self::$fs->mkdir( self::$dir_path );
				$cleared[] = true;
			}
		}

		$status = in_array(true, $cleared, true);

		if( ! $status ){
			self::log( 'Assets not deleted!' );
		}
		else {
			self::log( 'Assets successfully cleaned-up!' );
		}

		do_action('reycore/assets/cleanup', $status);

		return true;
	}

	public function clear__basic() {
		$this->clear_assets();
	}

	public function handle_clear_data(){
		if( isset($_REQUEST['clear_assets']) && absint($_REQUEST['clear_assets']) === 1 ){
			$this->clear_assets();
		}
	}

}