format
3 months ago
helpers
5 months ago
models
5 months ago
webp
3 months ago
class.backup.php
5 months ago
class.custom-folders.php
5 months ago
class.folder-image.php
5 months ago
class.folder.php
5 months ago
class.folders-list-table.php
5 months ago
class.gallery-nextgen.php
5 months ago
class.image-nextgen.php
5 months ago
class.image-statistic-folders.php
5 months ago
class.image-statistic-nextgen.php
5 months ago
class.wpcli-optimize.php
5 months ago
index.php
5 months ago
class.folder.php
379 lines
| 1 | <?php |
| 2 | |
| 3 | // Exit if accessed directly |
| 4 | if ( ! defined( 'ABSPATH' ) ) { |
| 5 | exit; |
| 6 | } |
| 7 | |
| 8 | /** |
| 9 | * Класс для работы с кастомными папками. |
| 10 | * |
| 11 | * @version 1.0 |
| 12 | */ |
| 13 | class WRIO_Folder { |
| 14 | |
| 15 | /** |
| 16 | * @var string Folder path. |
| 17 | */ |
| 18 | protected $path = ''; |
| 19 | |
| 20 | /** |
| 21 | * @var string SHA256 folder's path hash. |
| 22 | */ |
| 23 | protected $uid = ''; |
| 24 | |
| 25 | /** |
| 26 | * @var int Number of files in current folder. |
| 27 | */ |
| 28 | protected $files_count = 0; |
| 29 | |
| 30 | /** |
| 31 | * @var int Number of optimized files in current folder. |
| 32 | */ |
| 33 | protected $optimized_count = 0; |
| 34 | |
| 35 | /** |
| 36 | * @var int Number of errors in current folder. |
| 37 | */ |
| 38 | protected $errors_count = 0; |
| 39 | |
| 40 | /** |
| 41 | * WRIO_Folder constructor. |
| 42 | * |
| 43 | * @param array $params List of params set on the model. |
| 44 | */ |
| 45 | public function __construct( $params = [] ) { |
| 46 | // нужна проверка пути |
| 47 | foreach ( $params as $key => $value ) { |
| 48 | $this->set( $key, $value ); |
| 49 | } |
| 50 | if ( ! $this->uid ) { |
| 51 | $this->uid = hash( 'sha256', $this->path ); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | /** |
| 56 | * Get specified object's property. |
| 57 | * |
| 58 | * @param string $property_name Property name. |
| 59 | * |
| 60 | * @return bool |
| 61 | */ |
| 62 | public function get( $property_name ) { |
| 63 | if ( isset( $this->$property_name ) ) { |
| 64 | return $this->$property_name; |
| 65 | } |
| 66 | |
| 67 | return false; |
| 68 | } |
| 69 | |
| 70 | /** |
| 71 | * Set specified object's property. |
| 72 | * |
| 73 | * @param string $property_name Property name. |
| 74 | * @param mixed $value Value to set. |
| 75 | */ |
| 76 | public function set( $property_name, $value ) { |
| 77 | if ( isset( $this->$property_name ) ) { |
| 78 | $this->$property_name = $value; |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | /** |
| 83 | * Convert object to associative array form. |
| 84 | * |
| 85 | * @return array |
| 86 | */ |
| 87 | public function toArray() { |
| 88 | return [ |
| 89 | 'path' => $this->path, |
| 90 | 'files_count' => $this->files_count, |
| 91 | 'uid' => $this->uid, |
| 92 | 'optimized_count' => $this->optimized_count, |
| 93 | 'errors_count' => $this->errors_count, |
| 94 | ]; |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * Об� |
| 99 | одит каталог и добавляет в индекс файлы |
| 100 | * |
| 101 | * @param mixed $offset отступ от начала |
| 102 | * @param mixed $max_process_elements кол-во элементов за итерацию |
| 103 | * |
| 104 | * @return int Кол-во элементов, обработанны� |
| 105 | при индексировании |
| 106 | */ |
| 107 | public function indexing( $offset = 0, $max_process_elements = 100 ) { |
| 108 | global $wpdb; |
| 109 | |
| 110 | $iterator = $this->getRecursiveIterator(); |
| 111 | $allowed = $this->getAllowedFilesExt(); |
| 112 | $files = []; |
| 113 | $db_table = RIO_Process_Queue::table_name(); |
| 114 | |
| 115 | foreach ( $iterator as $file ) { |
| 116 | $ext = substr( $file, strrpos( strtolower( $file ), '.' ) + 1 ); // получаем расширение файла |
| 117 | if ( in_array( strtolower( $ext ), $allowed ) ) { |
| 118 | // сделать путь относительно корня |
| 119 | $files[] = $this->real_path_to_relative( $file->getPathname() ); |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | $files = array_slice( $files, $offset, $max_process_elements ); |
| 124 | foreach ( $files as $file ) { |
| 125 | $file = wp_normalize_path( $file ); |
| 126 | // $file_path = str_replace( $this->path, '', $file ); |
| 127 | $file_uid = hash( 'sha256', str_replace( wp_normalize_path( ABSPATH ), '', $file ) ); |
| 128 | $sql = $wpdb->prepare( "SELECT * FROM {$db_table} WHERE item_hash_alternative = %s AND item_hash = %s;", $this->uid, $file_uid ); |
| 129 | $row = $wpdb->get_row( $sql ); |
| 130 | |
| 131 | if ( empty( $row ) ) { |
| 132 | // если файла нет в индексе - добавляем |
| 133 | $extra_data = new WRIO_CF_Image_Extra_Data( |
| 134 | [ |
| 135 | 'file_path' => $file, |
| 136 | 'folder_relative_path' => $this->path, |
| 137 | ] |
| 138 | ); |
| 139 | $optimization_data = new RIO_Process_Queue( |
| 140 | [ |
| 141 | 'item_type' => 'cf_image', |
| 142 | 'item_hash' => $file_uid, // � |
| 143 | эш пути к файлу |
| 144 | 'item_hash_alternative' => $this->uid, // � |
| 145 | эш директории будет сделан сеттером |
| 146 | 'original_size' => 0, |
| 147 | 'final_size' => 0, |
| 148 | 'original_mime_type' => '', |
| 149 | 'final_mime_type' => '', |
| 150 | 'result_status' => 'unoptimized', |
| 151 | 'processing_level' => '', |
| 152 | 'extra_data' => $extra_data, |
| 153 | ] |
| 154 | ); |
| 155 | $optimization_data->save(); |
| 156 | } else { |
| 157 | // делаем апдейт и выставляем ласт индекс дату. Потом у кого в индексе ласт индекс дата меньше заданной, того уже нет на диске |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | return count( $files ); // сколько элементов обработано при индексировании. |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * Проверяет индекс на наличие несуществующи� |
| 166 | файлов. |
| 167 | * |
| 168 | * На� |
| 169 | одит файлы, которые пользователь удалил из папки, но в индексе они ещё есть. |
| 170 | * Удаляет из из индекса, вычитает из статистики |
| 171 | * |
| 172 | * @param mixed $offset отсутп от начала |
| 173 | * @param mixed $max_process_elements кол-во элементов за итерацию |
| 174 | * |
| 175 | * @return int $processed Кол-во обработанны� |
| 176 | записей. Используется для расчёта отступа |
| 177 | */ |
| 178 | public function syncIndex( $offset = 0, $max_process_elements = 100 ) { |
| 179 | global $wpdb; |
| 180 | $db_table = RIO_Process_Queue::table_name(); |
| 181 | $sql = $wpdb->prepare( "SELECT * FROM {$db_table} WHERE item_hash_alternative = %s LIMIT %d OFFSET %d;", $this->uid, $max_process_elements, $offset ); |
| 182 | $rows = $wpdb->get_results( $sql ); |
| 183 | $image_statistics = WRIO_Image_Statistic_Folders::get_instance(); |
| 184 | $processed = 0; |
| 185 | $deleted = 0; |
| 186 | |
| 187 | if ( ! empty( $rows ) ) { |
| 188 | foreach ( $rows as $row ) { |
| 189 | ++$processed; |
| 190 | $cf_image = new WRIO_Folder_Image( $row->id, $row ); |
| 191 | if ( ! $cf_image->isFileExists() ) { |
| 192 | if ( $cf_image->isOptimized() ) { |
| 193 | // если файл оптимизирован - вычитаем из статистики |
| 194 | $optimization_data = $cf_image->getOptimizationData(); |
| 195 | $optimized_size = $optimization_data->get_final_size(); |
| 196 | $original_size = $optimization_data->get_original_size(); |
| 197 | $webp_optimized_size = $optimization_data->get_extra_data()->get_webp_main_size(); |
| 198 | $image_statistics->deductFromField( 'webp_optimized_size', $webp_optimized_size ); |
| 199 | $image_statistics->deductFromField( 'optimized_size', $optimized_size ); |
| 200 | $image_statistics->deductFromField( 'original_size', $original_size ); |
| 201 | } |
| 202 | // если файла нет на диске - удаляем из индекса |
| 203 | $wpdb->delete( |
| 204 | $db_table, |
| 205 | [ |
| 206 | 'id' => $cf_image->get( 'id' ), |
| 207 | ], |
| 208 | [ '%d' ] |
| 209 | ); |
| 210 | ++$deleted; |
| 211 | } |
| 212 | } |
| 213 | $image_statistics->save(); |
| 214 | $this->reCountOptimizedFiles(); |
| 215 | } |
| 216 | $processed = $processed - $deleted; |
| 217 | |
| 218 | return $processed; |
| 219 | } |
| 220 | |
| 221 | /** |
| 222 | * Get allowed list of extensions. |
| 223 | * |
| 224 | * @return array |
| 225 | */ |
| 226 | public function getAllowedFilesExt() { |
| 227 | $allowed_formats = explode( ',', WRIO_Plugin::app()->getOption( 'allowed_formats', 'image/jpeg,image/png,image/gif' ) ); |
| 228 | $allowed = []; |
| 229 | foreach ( $allowed_formats as $format ) { |
| 230 | if ( $format == 'image/jpeg' ) { |
| 231 | $allowed[] = 'jpg'; |
| 232 | $allowed[] = 'jpeg'; |
| 233 | } elseif ( $format == 'image/png' ) { |
| 234 | $allowed[] = 'png'; |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | // $allowed = [ 'jpg', 'jpeg', 'png' ]; |
| 239 | |
| 240 | return $allowed; |
| 241 | } |
| 242 | |
| 243 | /** |
| 244 | * Count number of files in a folder. |
| 245 | * |
| 246 | * @return int |
| 247 | */ |
| 248 | public function countFiles() { |
| 249 | $iterator = $this->getRecursiveIterator(); |
| 250 | $allowed = $this->getAllowedFilesExt(); |
| 251 | $count = 0; |
| 252 | foreach ( $iterator as $file ) { |
| 253 | $ext = substr( $file, strrpos( strtolower( $file ), '.' ) + 1 ); // получаем расширение файла |
| 254 | if ( in_array( strtolower( $ext ), $allowed ) ) { |
| 255 | ++$count; |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | return $count; |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * Count number of indexes files. |
| 264 | * |
| 265 | * @return null|string |
| 266 | */ |
| 267 | public function countIndexedFiles() { |
| 268 | global $wpdb; |
| 269 | $db_table = RIO_Process_Queue::table_name(); |
| 270 | $sql_files = $wpdb->prepare( "SELECT COUNT(*) FROM {$db_table} WHERE item_type = %s AND item_hash_alternative = %s;", 'cf_image', $this->uid ); |
| 271 | $files_count = $wpdb->get_var( $sql_files ); |
| 272 | |
| 273 | return $files_count; |
| 274 | } |
| 275 | |
| 276 | public function reCountFiles() { |
| 277 | $this->files_count = $this->countFiles(); |
| 278 | |
| 279 | return $this->files_count; |
| 280 | } |
| 281 | |
| 282 | /** |
| 283 | * Recount optimized files. |
| 284 | * |
| 285 | * @return int|null|string |
| 286 | */ |
| 287 | public function reCountOptimizedFiles() { |
| 288 | global $wpdb; |
| 289 | $db_table = RIO_Process_Queue::table_name(); |
| 290 | $sql = "SELECT COUNT(*) FROM {$db_table} WHERE item_type = %s AND result_status = %s AND item_hash_alternative = %s;"; |
| 291 | $sql_prepared = $wpdb->prepare( $sql, 'cf_image', RIO_Process_Queue::STATUS_SUCCESS, $this->uid ); |
| 292 | $optimized_count = $wpdb->get_var( $sql_prepared ); |
| 293 | $this->optimized_count = $optimized_count; |
| 294 | |
| 295 | return $this->optimized_count; |
| 296 | } |
| 297 | |
| 298 | /** |
| 299 | * Remove folder and deduct its size from optimized and original size. |
| 300 | */ |
| 301 | public function remove() { |
| 302 | // удаляем файлы из индекса и переситываем стату |
| 303 | global $wpdb; |
| 304 | $db_table = RIO_Process_Queue::table_name(); |
| 305 | $sql = "SELECT SUM(original_size) AS original_size, SUM(final_size) AS optimized_size FROM {$db_table} WHERE item_type = %s AND result_status = %s AND item_hash_alternative = %s"; |
| 306 | $prepared_sql = $wpdb->prepare( $sql, 'cf_image', RIO_Process_Queue::STATUS_SUCCESS, $this->uid ); |
| 307 | $sum = $wpdb->get_row( $prepared_sql ); |
| 308 | |
| 309 | // Deduct from statistics |
| 310 | $image_statistics = WRIO_Image_Statistic_Folders::get_instance(); |
| 311 | $webp_optimized_size = WRIO_Plugin::app()->updateOption( 'webp_optimized_size', 0 ); |
| 312 | $image_statistics->deductFromField( 'webp_optimized_size', $webp_optimized_size ); |
| 313 | $image_statistics->deductFromField( 'optimized_size', $sum->optimized_size ); |
| 314 | $image_statistics->deductFromField( 'original_size', $sum->original_size ); |
| 315 | $image_statistics->save(); |
| 316 | |
| 317 | // Delete from db |
| 318 | $wpdb->delete( |
| 319 | $db_table, |
| 320 | [ |
| 321 | 'item_hash_alternative' => $this->uid, |
| 322 | 'item_type' => 'cf_image', |
| 323 | ], |
| 324 | [ '%s', '%s' ] |
| 325 | ); |
| 326 | } |
| 327 | |
| 328 | /** |
| 329 | * Get absolute path from relative. |
| 330 | * |
| 331 | * Same as 'wp_ajax_wriop_browse_dir'. |
| 332 | * |
| 333 | * @return bool|string |
| 334 | */ |
| 335 | public function real_path() { |
| 336 | if ( is_main_site() ) { |
| 337 | $base_path = get_home_path(); |
| 338 | } else { |
| 339 | $upload_dir = wp_upload_dir(); |
| 340 | $base_path = $upload_dir['basedir'] . '/'; |
| 341 | } |
| 342 | return realpath( $base_path . $this->path ); |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * Returns the directory path relative to the site root |
| 347 | * Input: /home/user/test/wp.com/wp-content/uploads/custom-folder/ |
| 348 | * Output: /wp-content/uploads/custom-folder/ |
| 349 | * |
| 350 | * @param string $path Путь к директории. Может быть абсолютным. |
| 351 | * |
| 352 | * Same as 'wp_ajax_wriop_browse_dir'. |
| 353 | * |
| 354 | * @return string $relative_path относительный путь |
| 355 | */ |
| 356 | public function real_path_to_relative( $path ) { |
| 357 | if ( is_main_site() ) { |
| 358 | $base_path = untrailingslashit( get_home_path() ); |
| 359 | } else { |
| 360 | $upload_dir = wp_upload_dir(); |
| 361 | $base_path = untrailingslashit( $upload_dir['basedir'] ); |
| 362 | } |
| 363 | $relative_path = str_replace( $base_path, '', $path ); |
| 364 | |
| 365 | return $relative_path; |
| 366 | } |
| 367 | |
| 368 | /** |
| 369 | * Get iterator to go scan files in directory. |
| 370 | * |
| 371 | * @return RecursiveIteratorIterator |
| 372 | */ |
| 373 | public function getRecursiveIterator() { |
| 374 | $iterator = new RecursiveDirectoryIterator( $this->real_path() ); |
| 375 | |
| 376 | return new RecursiveIteratorIterator( $iterator ); |
| 377 | } |
| 378 | } |
| 379 |