231 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			231 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace Sphere\Debloat;
 | |
| 
 | |
| /**
 | |
|  * Filesystem that mainly wraps the WP_File_System
 | |
|  * 
 | |
|  * @author  asadkn
 | |
|  * @since   1.0.0
 | |
|  * 
 | |
|  * @mixin \WP_Filesystem_Base
 | |
|  */
 | |
| class FileSystem
 | |
| {
 | |
| 	/**
 | |
| 	 * Pattern to remove protocol and www
 | |
| 	 */
 | |
| 	const PROTO_REMOVE_PATTERN = '#^(https?)?:?//(|www\.)#i';
 | |
| 
 | |
| 	/**
 | |
| 	 * @var WP_Filesystem_Base
 | |
| 	 */
 | |
| 	public $filesystem;
 | |
| 
 | |
| 	protected $valid_hosts;
 | |
| 	protected $paths_urls;
 | |
| 
 | |
| 	/**
 | |
| 	 * Setup file system
 | |
| 	 */
 | |
| 	public function __construct()
 | |
| 	{
 | |
| 		global $wp_filesystem;
 | |
| 
 | |
| 		if (empty($wp_filesystem)) {
 | |
| 
 | |
| 			require_once wp_normalize_path(ABSPATH . '/wp-admin/includes/file.php');
 | |
| 			
 | |
| 			// At shutdown is usually a ob_start callback which doesn't permit calling ob_*
 | |
| 			if (did_action('shutdown') && ob_get_level() > 0) {
 | |
| 				$creds = request_filesystem_credentials('');
 | |
| 			}
 | |
| 			else {
 | |
| 				ob_start();
 | |
| 				$creds = request_filesystem_credentials('');
 | |
| 				ob_end_clean();
 | |
| 			}
 | |
| 
 | |
| 			if (!$creds) {
 | |
| 				$creds = array();
 | |
| 			}
 | |
| 
 | |
| 			$filesystem = WP_Filesystem($creds);
 | |
| 			
 | |
| 			if (!$filesystem) {
 | |
| 				
 | |
| 				// Fallback to lax permissions
 | |
| 				$upload = wp_upload_dir();
 | |
| 				WP_Filesystem(false, $upload['basedir'], true);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		$this->filesystem = $wp_filesystem;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Recursively make parent directories for a path.
 | |
| 	 * 
 | |
| 	 * Note: Adapted a bit from wp_mkdir_p().
 | |
| 	 *
 | |
| 	 * @param string $path
 | |
| 	 * @return boolean  true on success, false or failure.
 | |
| 	 */
 | |
| 	public function mkdir_p($path)
 | |
| 	{
 | |
| 		if ($this->is_dir($path)) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$path = str_replace('//', '/', $path);
 | |
| 
 | |
| 		// Safe mode safety.
 | |
| 		$path = rtrim($path, '/');
 | |
| 		$path = $path ?: '/';
 | |
| 
 | |
| 		$created = $this->mkdir($path, FS_CHMOD_DIR);
 | |
| 		if ($created) {
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		// If it failed above, try again by creating the parent first, recursively.
 | |
| 		$parent = dirname($path);
 | |
| 		if ($parent !== '/' && $this->mkdir_p($parent)) {
 | |
| 			return $this->mkdir($path, FS_CHMOD_DIR);
 | |
| 		}
 | |
| 
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get a local file by the provided URL, if possible.
 | |
| 	 * 
 | |
| 	 * @see wp_normalize_path()
 | |
| 	 * 
 | |
| 	 * @return bool|string  Either the path or false on failure.
 | |
| 	 */
 | |
| 	public function url_to_local($url)
 | |
| 	{
 | |
| 		$url = trim($url);
 | |
| 		
 | |
| 		// Not a URL, just return the path.
 | |
| 		if (substr($url, 0, 4) !== 'http' && substr($url, 0, 2) !== '//') {
 | |
| 			return $url;
 | |
| 		}
 | |
| 
 | |
| 		$url = explode('?', trim($url));
 | |
| 		$url = trim($url[0]);
 | |
| 
 | |
| 		// We're not working with encoded URLs.
 | |
| 		if (strpos($url, '%') !== false) {
 | |
|             $url = urldecode($url);
 | |
| 		}
 | |
| 
 | |
| 		// Add https:// for parse_url() or it fails.
 | |
| 		$url_no_proto = preg_replace(self::PROTO_REMOVE_PATTERN, '', $url);
 | |
| 		$url_host     = parse_url('https://' . $url_no_proto, PHP_URL_HOST);
 | |
| 
 | |
| 		// Not a known host / URL.
 | |
| 		if (!in_array($url_host, $this->get_valid_hosts())) {
 | |
| 			return false;
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * Go through each known path url map and stop at first matched.
 | |
| 		 */
 | |
| 		$valid_urls   = $this->get_paths_urls();
 | |
| 		$url_dirname  = dirname($url_no_proto);
 | |
| 		$matched      = [];
 | |
| 
 | |
| 		foreach ($valid_urls as $path_url) {
 | |
| 
 | |
| 			if (strpos($url_dirname, untrailingslashit($path_url['url'])) !== false) {
 | |
| 				$matched = $path_url;
 | |
| 				break;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// We have a matched path.
 | |
| 		if (!empty($matched['path'])) {
 | |
| 			$path = wp_normalize_path(
 | |
| 				$matched['path'] . str_replace($matched['url'], '', $url_dirname)
 | |
| 			);
 | |
| 
 | |
| 			$file = trailingslashit($path) . wp_basename($url_no_proto);
 | |
| 
 | |
| 			if (file_exists($file) && is_file($file) && is_readable($file)) {
 | |
| 				return $file;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get recognized hostnames for stylesheet URLs.
 | |
| 	 * 
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public function get_valid_hosts()
 | |
| 	{
 | |
| 		if (!$this->valid_hosts) {
 | |
| 			$this->valid_hosts = wp_list_pluck(
 | |
| 				$this->get_paths_urls(),
 | |
| 				'host'
 | |
| 			);
 | |
| 		}
 | |
| 
 | |
| 		return apply_filters('debloat/file_system/valid_hosts', $this->valid_hosts);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Get a map of known path URLs, associated local path, and host.
 | |
| 	 * 
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public function get_paths_urls()
 | |
| 	{
 | |
| 		if (!$this->paths_urls) {
 | |
| 
 | |
| 			// We add https:// back for parse_url() to prevent it from failing
 | |
| 			$site_url  = preg_replace(self::PROTO_REMOVE_PATTERN, '', site_url());
 | |
| 			$site_host = parse_url('https://' . $site_url, PHP_URL_HOST);
 | |
| 
 | |
| 			$content_url  = preg_replace(self::PROTO_REMOVE_PATTERN, '', content_url());
 | |
| 			$content_host = parse_url('https://' . $content_url, PHP_URL_HOST);
 | |
| 
 | |
| 			/**
 | |
| 			 * This array will be processed in order it's defined to find the matching host and URL.
 | |
| 			 */
 | |
| 			$hosts = [
 | |
| 
 | |
| 				// First priority to use content_host and content_url()
 | |
| 				'content' => [
 | |
| 					'url'  => $content_url, 
 | |
| 					'path' => WP_CONTENT_DIR,
 | |
| 					'host' => $content_host
 | |
| 				],
 | |
| 				
 | |
| 				// Fallback to using site URL with ABSPATH
 | |
| 				'site' => [
 | |
| 					'url'  => $site_url, 
 | |
| 					'path' => ABSPATH,
 | |
| 					'host' => $site_host
 | |
| 				],
 | |
| 			];
 | |
| 
 | |
| 			$this->paths_urls  = apply_filters('debloat/file_system/paths_urls', $hosts);
 | |
| 		}
 | |
| 
 | |
| 		return $this->paths_urls;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Proxies to WP_Filesystem_Base
 | |
| 	 */
 | |
| 	public function __call($name, $arguments)
 | |
| 	{
 | |
| 		return call_user_func_array([$this->filesystem, $name], $arguments);
 | |
| 	}
 | |
| 
 | |
| } |