|
| 1 | +<?php |
| 2 | +namespace EnableMediaReplace\FileSystem\Controller; |
| 3 | +use EnableMediaReplace\ShortpixelLogger\ShortPixelLogger as Log; |
| 4 | + |
| 5 | +use EnableMediaReplace\FileSystem\Model\File\DirectoryModel as DirectoryModel; |
| 6 | +use EnableMediaReplace\FileSystem\Model\File\FileModel as FileModel; |
| 7 | + |
| 8 | + |
| 9 | +/** Controller for FileSystem operations |
| 10 | +* |
| 11 | +* This controller is used for -compound- ( complex ) FS operations, using the provided models File en Directory. |
| 12 | +* USE via \wpSPIO()->filesystem(); |
| 13 | +*/ |
| 14 | +Class FileSystemController |
| 15 | +{ |
| 16 | + |
| 17 | + public function __construct() |
| 18 | + { |
| 19 | + |
| 20 | + } |
| 21 | + |
| 22 | + /** Get FileModel for a certain path. This can exist or not |
| 23 | + * |
| 24 | + * @param String Path Full Path to the file |
| 25 | + * @return FileModel FileModel Object. If file does not exist, not all values are set. |
| 26 | + */ |
| 27 | + public function getFile($path) |
| 28 | + { |
| 29 | + return new FileModel($path); |
| 30 | + } |
| 31 | + |
| 32 | + |
| 33 | + |
| 34 | + /** Get DirectoryModel for a certain path. This can exist or not |
| 35 | + * |
| 36 | + * @param String $path Full Path to the Directory. |
| 37 | + * @return DirectoryModel Object with status set on current directory. |
| 38 | + */ |
| 39 | + public function getDirectory($path) |
| 40 | + { |
| 41 | + return new DirectoryModel($path); |
| 42 | + } |
| 43 | + |
| 44 | + |
| 45 | + |
| 46 | + |
| 47 | + /** This function returns the WordPress Basedir for uploads ( without date and such ) |
| 48 | + * Normally this would point to /wp-content/uploads. |
| 49 | + * @returns DirectoryModel |
| 50 | + */ |
| 51 | + public function getWPUploadBase() |
| 52 | + { |
| 53 | + $upload_dir = wp_upload_dir(null, false); |
| 54 | + |
| 55 | + return $this->getDirectory($upload_dir['basedir']); |
| 56 | + } |
| 57 | + |
| 58 | + /** This function returns the Absolute Path of the WordPress installation where the **CONTENT** directory is located. |
| 59 | + * Normally this would be the same as ABSPATH, but there are installations out there with -cough- alternative approaches |
| 60 | + * @returns DirectoryModel Either the ABSPATH or where the WP_CONTENT_DIR is located |
| 61 | + */ |
| 62 | + public function getWPAbsPath() |
| 63 | + { |
| 64 | + $wpContentAbs = str_replace( 'wp-content', '', WP_CONTENT_DIR); |
| 65 | + if (ABSPATH == $wpContentAbs) |
| 66 | + $abspath = ABSPATH; |
| 67 | + else |
| 68 | + $abspath = $wpContentAbs; |
| 69 | + |
| 70 | + if (defined('UPLOADS')) // if this is set, lead. |
| 71 | + $abspath = trailingslashit(ABSPATH) . UPLOADS; |
| 72 | + |
| 73 | + $abspath = apply_filters('shortpixel/filesystem/abspath', $abspath ); |
| 74 | + |
| 75 | + return $this->getDirectory($abspath); |
| 76 | + } |
| 77 | + |
| 78 | + |
| 79 | + /** Utility function that tries to convert a file-path to a webURL. |
| 80 | + * |
| 81 | + * If possible, rely on other better methods to find URL ( e.g. via WP functions ). |
| 82 | + */ |
| 83 | + public function pathToUrl(FileModel $file) |
| 84 | + { |
| 85 | + $filepath = $file->getFullPath(); |
| 86 | + $directory = $file->getFileDir(); |
| 87 | + |
| 88 | + $is_multi_site = $this->env->is_multisite; |
| 89 | + $is_main_site = $this->env->is_mainsite; |
| 90 | + |
| 91 | + // stolen from wp_get_attachment_url |
| 92 | + if ( ( $uploads = wp_get_upload_dir() ) && (false === $uploads['error'] || strlen(trim($uploads['error'])) == 0 ) ) { |
| 93 | + // Check that the upload base exists in the file location. |
| 94 | + if ( 0 === strpos( $filepath, $uploads['basedir'] ) ) { // Simple as it should, filepath and basedir share. |
| 95 | + // Replace file location with url location. |
| 96 | + $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $filepath ); |
| 97 | + } |
| 98 | + // Multisite backups are stored under uploads/ShortpixelBackups/etc , but basedir would include uploads/sites/2 etc, not matching above |
| 99 | + // If this is case, test if removing the last two directories will result in a 'clean' uploads reference. |
| 100 | + // This is used by getting preview path ( backup pathToUrl) in bulk and for comparer.. |
| 101 | + elseif ($is_multi_site && ! $is_main_site && 0 === strpos($filepath, dirname(dirname($uploads['basedir']))) ) |
| 102 | + { |
| 103 | + |
| 104 | + $url = str_replace( dirname(dirname($uploads['basedir'])), dirname(dirname($uploads['baseurl'])), $filepath ); |
| 105 | + $homeUrl = home_url(); |
| 106 | + |
| 107 | + // The result didn't end in a full URL because URL might have less subdirs ( dirname dirname) . |
| 108 | + // This happens when site has blogs.dir (sigh) on a subdomain . Try to substitue the ABSPATH root with the home_url |
| 109 | + if (strpos($url, $homeUrl) === false) |
| 110 | + { |
| 111 | + $url = str_replace( trailingslashit(ABSPATH), trailingslashit($homeUrl), $filepath); |
| 112 | + } |
| 113 | + |
| 114 | + } elseif ( false !== strpos( $filepath, 'wp-content/uploads' ) ) { |
| 115 | + // Get the directory name relative to the basedir (back compat for pre-2.7 uploads) |
| 116 | + $url = trailingslashit( $uploads['baseurl'] . '/' . _wp_get_attachment_relative_path( $filepath ) ) . wp_basename( $filepath ); |
| 117 | + } else { |
| 118 | + // It's a newly-uploaded file, therefore $file is relative to the basedir. |
| 119 | + $url = $uploads['baseurl'] . "/$filepath"; |
| 120 | + } |
| 121 | + } |
| 122 | + |
| 123 | + $wp_home_path = (string) $this->getWPAbsPath(); |
| 124 | + // If the whole WP homepath is still in URL, assume the replace when wrong ( not replaced w/ URL) |
| 125 | + // This happens when file is outside of wp_uploads_dir |
| 126 | + if (strpos($url, $wp_home_path) !== false) |
| 127 | + { |
| 128 | + // This is SITE URL, for the same reason it should be home_url in FILEMODEL. The difference is when the site is running on a subdirectory |
| 129 | + // (1) ** This is a fix for a real-life issue, do not change if this causes issues, another fix is needed then. |
| 130 | + // (2) ** Also a real life fix when a path is /wwwroot/assets/sites/2/ etc, in get site url, the home URL is the site URL, without appending the sites stuff. Fails on original image. |
| 131 | + if ($is_multi_site && ! $is_main_site) |
| 132 | + { |
| 133 | + $wp_home_path = wp_normalize_path(trailingslashit($uploads['basedir'])); |
| 134 | + $home_url = trailingslashit($uploads['baseurl']); |
| 135 | + } |
| 136 | + else |
| 137 | + $home_url = trailingslashit(get_site_url()); // (1) |
| 138 | + $url = str_replace($wp_home_path, $home_url, $filepath); |
| 139 | + } |
| 140 | + |
| 141 | + // can happen if there are WP path errors. |
| 142 | + if (is_null($url)) |
| 143 | + return false; |
| 144 | + |
| 145 | + $parsed = parse_url($url); // returns array, null, or false. |
| 146 | + |
| 147 | + // Some hosts set the content dir to a relative path instead of a full URL. Api can't handle that, so add domain and such if this is the case. |
| 148 | + if ( !isset($parsed['scheme']) ) {//no absolute URLs used -> we implement a hack |
| 149 | + |
| 150 | + if (isset($parsed['host'])) // This is for URL's for // without http or https. hackhack. |
| 151 | + { |
| 152 | + $scheme = is_ssl() ? 'https:' : 'http:'; |
| 153 | + return $scheme. $url; |
| 154 | + } |
| 155 | + else |
| 156 | + { |
| 157 | + // From Metafacade. Multiple solutions /hacks. |
| 158 | + $home_url = trailingslashit((function_exists("is_multisite") && is_multisite()) ? trim(network_site_url("/")) : trim(home_url())); |
| 159 | + return $home_url . ltrim($url,'/');//get the file URL |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + if (! is_null($parsed) && $parsed !== false) |
| 164 | + return $url; |
| 165 | + |
| 166 | + return false; |
| 167 | + } |
| 168 | + |
| 169 | + /** Utility function to check if a path is an URL |
| 170 | + * Checks if this path looks like an URL. |
| 171 | + * @param $path String Path to check |
| 172 | + * @return Boolean If path seems domain. |
| 173 | + */ |
| 174 | + public function pathIsUrl($path) |
| 175 | + { |
| 176 | + $is_http = (substr($path, 0, 4) == 'http') ? true : false; |
| 177 | + $is_https = (substr($path, 0, 5) == 'https') ? true : false; |
| 178 | + $is_neutralscheme = (substr($path, 0, 2) == '//') ? true : false; // when URL is relative like //wp-content/etc |
| 179 | + $has_urldots = (strpos($path, '://') !== false) ? true : false; // Like S3 offloads |
| 180 | + |
| 181 | + if ($is_http || $is_https || $is_neutralscheme || $has_urldots) |
| 182 | + return true; |
| 183 | + else |
| 184 | + return false; |
| 185 | + } |
| 186 | + |
| 187 | + /** Sort files / directories in a certain way. |
| 188 | + * Future dev to include options via arg. |
| 189 | + */ |
| 190 | + public function sortFiles($array, $args = array() ) |
| 191 | + { |
| 192 | + if (count($array) == 0) |
| 193 | + return $array; |
| 194 | + |
| 195 | + // what are we sorting. |
| 196 | + $class = get_class($array[0]); |
| 197 | + $is_files = ($class == 'EnableMediaReplace\FileModel') ? true : false; // if not files, then dirs. |
| 198 | + |
| 199 | + usort($array, function ($a, $b) use ($is_files) |
| 200 | + { |
| 201 | + if ($is_files) |
| 202 | + return strcmp($a->getFileName(), $b->getFileName()); |
| 203 | + else { |
| 204 | + return strcmp($a->getName(), $b->getName()); |
| 205 | + } |
| 206 | + } |
| 207 | + ); |
| 208 | + |
| 209 | + return $array; |
| 210 | + |
| 211 | + } |
| 212 | + |
| 213 | + public function downloadFile($url, $destinationPath) |
| 214 | + { |
| 215 | + |
| 216 | + //$downloadTimeout = defined(SHORTPIXEL_MAX_EXECUTION_TIME) ? max(SHORTPIXEL_MAX_EXECUTION_TIME - 10, 15) :; |
| 217 | + $max_exec = intval(ini_get('max_execution_time')); |
| 218 | + if ($max_exec === 0) // max execution time of zero means infinite. Quantify. |
| 219 | + $max_exec = 60; |
| 220 | + elseif($max_exec < 0) // some hosts like to set negative figures on this. Ignore that. |
| 221 | + $max_exec = 30; |
| 222 | + |
| 223 | + $downloadTimeout = $max_exec; |
| 224 | + |
| 225 | + |
| 226 | + $destinationFile = $this->getFile($destinationPath); |
| 227 | + |
| 228 | + $args_for_get = array( |
| 229 | + 'stream' => true, |
| 230 | + 'filename' => $destinationPath, |
| 231 | + ); |
| 232 | + |
| 233 | + $response = wp_remote_get( $url, $args_for_get ); |
| 234 | + |
| 235 | + if(is_wp_error( $response )) { |
| 236 | + Log::addError('Download file failed', array($url, $response->get_error_messages(), $response->get_error_codes() )); |
| 237 | + |
| 238 | + // Try to get it then via this way. |
| 239 | + $response = download_url($url, $downloadTimeout); |
| 240 | + if (!is_wp_error($response)) // response when alright is a tmp filepath. But given path can't be trusted since that can be reason for fail. |
| 241 | + { |
| 242 | + $tmpFile = $this->getFile($response); |
| 243 | + $result = $tmpFile->move($destinationFile); |
| 244 | + |
| 245 | + } // download_url .. |
| 246 | + else { |
| 247 | + Log::addError('Secondary download failed', array($url, $response->get_error_messages(), $response->get_error_codes() )); |
| 248 | + } |
| 249 | + } |
| 250 | + else { // success, at least the download. |
| 251 | + $destinationFile = $this->getFile($response['filename']); |
| 252 | + } |
| 253 | + |
| 254 | + Log::addDebug('Remote Download attempt result', array($url, $destinationPath)); |
| 255 | + if ($destinationFile->exists()) |
| 256 | + return true; |
| 257 | + else |
| 258 | + return false; |
| 259 | + } |
| 260 | + |
| 261 | + |
| 262 | + |
| 263 | + /** Get all files from a directory tree, starting at given dir. |
| 264 | + * @param DirectoryModel $dir to recursive into |
| 265 | + * @param Array $filters Collection of optional filters as accepted by FileFilter in directoryModel |
| 266 | + * @return Array Array of FileModel Objects |
| 267 | + **/ |
| 268 | + public function getFilesRecursive(DirectoryModel $dir, $filters = array() ) |
| 269 | + { |
| 270 | + $fileArray = array(); |
| 271 | + |
| 272 | + if (! $dir->exists()) |
| 273 | + return $fileArray; |
| 274 | + |
| 275 | + $files = $dir->getFiles($filters); |
| 276 | + $fileArray = array_merge($fileArray, $files); |
| 277 | + |
| 278 | + $subdirs = $dir->getSubDirectories(); |
| 279 | + |
| 280 | + foreach($subdirs as $subdir) |
| 281 | + { |
| 282 | + $fileArray = array_merge($fileArray, $this->getFilesRecursive($subdir, $filters)); |
| 283 | + } |
| 284 | + |
| 285 | + return $fileArray; |
| 286 | + } |
| 287 | + |
| 288 | + // Url very sparingly. |
| 289 | + public function url_exists($url) |
| 290 | + { |
| 291 | + if (! function_exists('curl_init')) |
| 292 | + { |
| 293 | + return null; |
| 294 | + } |
| 295 | + |
| 296 | + $ch = curl_init($url); |
| 297 | + curl_setopt($ch, CURLOPT_NOBODY, true); |
| 298 | + curl_exec($ch); |
| 299 | + $responseCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); |
| 300 | + curl_close($ch); |
| 301 | + |
| 302 | + if ($responseCode == 200) |
| 303 | + { |
| 304 | + return true; |
| 305 | + } |
| 306 | + else { |
| 307 | + return false; |
| 308 | + } |
| 309 | + |
| 310 | + } |
| 311 | +} |
0 commit comments