259 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace Sphere\Debloat;
 | |
| 
 | |
| /**
 | |
|  * Debloat processing setup for JS and CSS optimizations.
 | |
|  * 
 | |
|  * @author  asadkn
 | |
|  * @since   1.0.0
 | |
|  */
 | |
| class Process
 | |
| {
 | |
| 	/**
 | |
| 	 * Init happens too early around plugins_loaded
 | |
| 	 */
 | |
| 	public function init()
 | |
| 	{
 | |
| 		// Setup a few extra options.
 | |
| 		Plugin::options()->delay_css_type = 'interact';
 | |
| 		Plugin::options()->delay_js_type  = 'interact';
 | |
| 
 | |
| 		// Setup at init but before template_redirect.
 | |
| 		add_action('init', [$this, 'setup']);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Setup filters for processing
 | |
| 	 * 
 | |
| 	 * @since 1.1.0
 | |
| 	 */
 | |
| 	public function setup()
 | |
| 	{
 | |
| 		if ($this->should_process()) {
 | |
| 
 | |
| 			// Load integrations.
 | |
| 			$integrations = array_unique(array_merge(
 | |
| 				(array) Plugin::options()->integrations_js,
 | |
| 				(array) Plugin::options()->integrations_css
 | |
| 			));
 | |
| 
 | |
| 			if ($integrations) {
 | |
| 				if (in_array('elementor', $integrations) && class_exists('\Elementor\Plugin', false)) {
 | |
| 					new Integrations\Elementor;
 | |
| 				}
 | |
| 
 | |
| 				if (in_array('wpbakery', $integrations) && class_exists('Vc_Manager')) {
 | |
| 					new Integrations\Wpbakery;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			/**
 | |
| 			 * Process HTML for inline and local stylesheets.
 | |
| 			 * 
 | |
| 			 * wp_ob_end_flush_all() will take care of flushing it.
 | |
| 			 * 
 | |
| 			 * Note: Autoptimize starts at priority 2 so we use 3 to process BEFORE AO.
 | |
| 			 */
 | |
| 			add_action('template_redirect', function() {
 | |
| 				if (!apply_filters('debloat/should_process', true)) {
 | |
| 					return false;
 | |
| 				}
 | |
| 
 | |
| 				// Can't go in should_process() as that's too early.
 | |
| 				if (function_exists('\amp_is_request') && \amp_is_request()) {
 | |
| 					return false;
 | |
| 				}
 | |
| 
 | |
| 				// Shouldn't process feeds, embeds (iframe), or robots.txt request.
 | |
| 				if (\is_feed() || \is_embed() || \is_robots()) {
 | |
| 					return false;
 | |
| 				}
 | |
| 
 | |
| 				ob_start([$this, 'process_markup']);
 | |
| 			}, -999);
 | |
| 
 | |
| 			// DEBUG: Devs if your output is disappearing - which you need for debugging,
 | |
| 			// uncomment below and comment the init action above.
 | |
| 			// add_action('template_redirect', function() { ob_start(); }, -999);
 | |
| 			// add_action('shutdown', function() {
 | |
| 			// 	$content = ob_get_clean();
 | |
| 			// 	echo $this->process_markup($content);
 | |
| 			// }, -10);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Process DOM Markup provided with the html.
 | |
| 	 * 
 | |
| 	 * @param  string $html
 | |
| 	 * @return string
 | |
| 	 */
 | |
| 	public function process_markup($html)
 | |
| 	{
 | |
| 		do_action('debloat/process_markup', $this);
 | |
| 
 | |
| 		if (!$this->is_valid_markup($html)) {
 | |
| 			return $html;
 | |
| 		}
 | |
| 
 | |
| 		$dom = null;
 | |
| 		
 | |
| 		if ($this->should_optimize_css()) {
 | |
| 			$dom      = $this->get_dom($html);
 | |
| 			$optimize = new OptimizeCss($dom, $html);
 | |
| 			$html     = $optimize->process();
 | |
| 		}
 | |
| 
 | |
| 		if ($this->should_optimize_js()) {
 | |
| 			$optimize_js = new OptimizeJs($html);
 | |
| 			$html = $optimize_js->process();
 | |
| 		}
 | |
| 
 | |
| 		// Add delay load JS and extras as needed.
 | |
| 		$html = Plugin::delay_load()->render($html);
 | |
| 
 | |
| 		// Failed at processing DOM, return original.
 | |
| 		if (!$dom) {
 | |
| 			return $html;
 | |
| 		}
 | |
| 
 | |
| 		return $html;
 | |
| 	}
 | |
| 
 | |
| 	public function is_valid_markup($html)
 | |
| 	{
 | |
| 		if (stripos($html, '<html') === false) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get DOM object for the provided HTML.
 | |
| 	 *
 | |
| 	 * @param string $html
 | |
| 	 * @return boolean|\DOMDocument
 | |
| 	 */
 | |
| 	protected function get_dom($html)
 | |
| 	{
 | |
| 		if (!$html) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		$libxml_previous = libxml_use_internal_errors(true);
 | |
| 		$dom    = new \DOMDocument();
 | |
| 		$result = $dom->loadHTML($html);
 | |
| 
 | |
| 		libxml_clear_errors();
 | |
| 		libxml_use_internal_errors($libxml_previous);
 | |
| 
 | |
| 		// Deprecating xpath querying.
 | |
| 		// if ($result) {
 | |
| 			// $dom->xpath = new \DOMXPath($dom);
 | |
| 		// }
 | |
| 
 | |
| 		return $result ? $dom : false;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Should any processing be done at all.
 | |
| 	 * 
 | |
| 	 * @return boolean
 | |
| 	 */
 | |
| 	public function should_process()
 | |
| 	{
 | |
| 		if (is_admin()) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		if (function_exists('is_customize_preview') && is_customize_preview()) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		if (isset($_GET['nodebloat'])) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		if (Util\is_elementor()) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		// WPBakery Page Builder. vc_is_page_editable() isn't reliable at all hooks.
 | |
|         if (!empty($_GET['vc_editable'])) {
 | |
| 			return false;
 | |
|         }
 | |
| 
 | |
| 		if (Plugin::options()->disable_for_admins && current_user_can('manage_options')) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Should the JS be optimized.
 | |
| 	 *
 | |
| 	 * @return boolean
 | |
| 	 */
 | |
| 	public function should_optimize_js()
 | |
| 	{
 | |
| 		$valid = true;
 | |
| 		return apply_filters('debloat/should_optimize_js', $valid);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Should the CSS be optimized.
 | |
| 	 *
 | |
| 	 * @return boolean
 | |
| 	 */
 | |
| 	public function should_optimize_css()
 | |
| 	{
 | |
| 		$valid = Plugin::options()->remove_css || Plugin::options()->optimize_css;
 | |
| 		return apply_filters('debloat/should_optimize_css', $valid);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Conditions test to see if current page matches in the provided valid conditions.
 | |
| 	 *
 | |
| 	 * @param array $enable_on
 | |
| 	 * @return boolean
 | |
| 	 */
 | |
| 	public function check_enabled(array $enable_on)
 | |
| 	{
 | |
| 		if (in_array('all', $enable_on)) {
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		$conditions = [
 | |
| 			'pages'      => 'is_page',
 | |
| 			'posts'      => 'is_single',
 | |
| 			'archives'   => 'is_archive',
 | |
| 			'archive'    => 'is_archive', // Alias
 | |
| 			'categories' => 'is_category',
 | |
| 			'tags'       => 'is_tag',
 | |
| 			'search'     => 'is_search',
 | |
| 			'404'        => 'is_404',
 | |
| 			'home' => function() {
 | |
| 				return is_home() || is_front_page();
 | |
| 			},
 | |
| 		];
 | |
| 
 | |
| 		$satisfy = false;
 | |
| 		foreach ($enable_on as $key) {
 | |
| 			if (!isset($conditions[$key]) || !is_callable($conditions[$key])) {
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			$satisfy = call_user_func($conditions[$key]);
 | |
| 			
 | |
| 			// Stop going further in loop once satisfied.
 | |
| 			if ($satisfy) {
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $satisfy;
 | |
| 	}
 | |
| } |