Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
37.25% |
57 / 153 |
|
18.18% |
6 / 33 |
CRAP | |
0.00% |
0 / 1 |
| Tus_File | |
37.09% |
56 / 151 |
|
18.18% |
6 / 33 |
1150.75 | |
0.00% |
0 / 1 |
| __construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
| ensure_integer | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
| set_meta | |
88.89% |
8 / 9 |
|
0.00% |
0 / 1 |
2.01 | |||
| set_name | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
| get_name | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| set_file_size | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
| get_file_size | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| set_key | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
2.06 | |||
| get_key | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| set_checksum | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
| get_checksum | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| set_offset | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
| get_offset | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| set_location | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
| get_location | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| set_file_path | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| get_file_path | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| set_upload_metadata | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
| get_input_stream | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| set_input_stream | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| details | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
2 | |||
| upload | |
65.22% |
15 / 23 |
|
0.00% |
0 / 1 |
9.06 | |||
| open | |
60.00% |
6 / 10 |
|
0.00% |
0 / 1 |
5.02 | |||
| exists | |
57.14% |
4 / 7 |
|
0.00% |
0 / 1 |
6.97 | |||
| seek | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
| read | |
80.00% |
4 / 5 |
|
0.00% |
0 / 1 |
2.03 | |||
| write | |
75.00% |
3 / 4 |
|
0.00% |
0 / 1 |
3.14 | |||
| merge | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
12 | |||
| copy | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 | |||
| delete | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
| delete_files | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
30 | |||
| close | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
| get_wp_filesystem | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * Main |
| 4 | * |
| 5 | * @package VideoPressUploader |
| 6 | **/ |
| 7 | |
| 8 | namespace VideoPressUploader; |
| 9 | |
| 10 | use InvalidArgumentException; |
| 11 | |
| 12 | // Avoid direct calls to this file. |
| 13 | if ( ! defined( 'ABSPATH' ) ) { |
| 14 | die( 0 ); |
| 15 | } |
| 16 | |
| 17 | /** |
| 18 | * Class Tus_File. |
| 19 | * |
| 20 | * @package VideoPressUploader |
| 21 | */ |
| 22 | class Tus_File { |
| 23 | const CHUNK_SIZE = 8192; // 8 kilobytes. |
| 24 | const INPUT_STREAM = 'php://input'; |
| 25 | const READ_BINARY = 'rb'; |
| 26 | const APPEND_BINARY = 'ab'; |
| 27 | |
| 28 | /** |
| 29 | * The input stream. |
| 30 | * |
| 31 | * @var string |
| 32 | */ |
| 33 | protected static $input_stream = self::INPUT_STREAM; |
| 34 | |
| 35 | /** |
| 36 | * The key. |
| 37 | * |
| 38 | * @var string The key. |
| 39 | */ |
| 40 | protected $key; |
| 41 | |
| 42 | /** |
| 43 | * The file checksum. |
| 44 | * |
| 45 | * @var string |
| 46 | */ |
| 47 | protected $checksum; |
| 48 | |
| 49 | /** |
| 50 | * The file name. |
| 51 | * |
| 52 | * @var string |
| 53 | */ |
| 54 | protected $name; |
| 55 | |
| 56 | /** |
| 57 | * The cache we are using. |
| 58 | * |
| 59 | * @var Tus_Abstract_Cache |
| 60 | */ |
| 61 | protected $cache; |
| 62 | |
| 63 | /** |
| 64 | * The current file offset. |
| 65 | * |
| 66 | * @var int |
| 67 | */ |
| 68 | protected $offset; |
| 69 | |
| 70 | /** |
| 71 | * The location. |
| 72 | * |
| 73 | * @var string |
| 74 | */ |
| 75 | protected $location; |
| 76 | |
| 77 | /** |
| 78 | * The file path. |
| 79 | * |
| 80 | * @var string |
| 81 | */ |
| 82 | protected $file_path; |
| 83 | |
| 84 | /** |
| 85 | * The file size. |
| 86 | * |
| 87 | * @var int |
| 88 | */ |
| 89 | protected $file_size; |
| 90 | |
| 91 | /** |
| 92 | * The upload metadata. |
| 93 | * |
| 94 | * @var array |
| 95 | */ |
| 96 | private $upload_metadata = array(); |
| 97 | |
| 98 | /** |
| 99 | * File constructor. |
| 100 | * |
| 101 | * @param string|null $name Name. |
| 102 | * @param Tus_Abstract_Cache|null $cache Cache. |
| 103 | */ |
| 104 | public function __construct( $name = null, $cache = null ) { |
| 105 | $this->name = $name; |
| 106 | $this->cache = $cache; |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Returns an integer if it's castable. Otherwise it throws |
| 111 | * |
| 112 | * @param string|int $number Number to cast. |
| 113 | * @throws InvalidArgumentException If argument is invalid. |
| 114 | * @return int |
| 115 | */ |
| 116 | public function ensure_integer( $number ) { |
| 117 | if ( ! is_numeric( $number ) ) { |
| 118 | throw new InvalidArgumentException( 'argument needs to be an integer. Check stacktrace' ); |
| 119 | } |
| 120 | return (int) $number; |
| 121 | } |
| 122 | |
| 123 | /** |
| 124 | * Set file meta. |
| 125 | * |
| 126 | * @param int $offset Offset. |
| 127 | * @param int $file_size File size. |
| 128 | * @param string $file_path File path. |
| 129 | * @param string $location Location. |
| 130 | * |
| 131 | * @throws InvalidArgumentException If argument is invalid. |
| 132 | * @return $this |
| 133 | */ |
| 134 | public function set_meta( $offset, $file_size, $file_path, $location = null ) { |
| 135 | $offset = $this->ensure_integer( $offset ); |
| 136 | $file_size = $this->ensure_integer( $file_size ); |
| 137 | |
| 138 | if ( ! is_string( $file_path ) ) { |
| 139 | throw new InvalidArgumentException( '$file_path needs to be a string' ); |
| 140 | } |
| 141 | |
| 142 | $this->offset = absint( $offset ); |
| 143 | $this->file_size = absint( $file_size ); |
| 144 | $this->file_path = $file_path; |
| 145 | $this->location = $location; |
| 146 | |
| 147 | return $this; |
| 148 | } |
| 149 | |
| 150 | /** |
| 151 | * Set name. |
| 152 | * |
| 153 | * @param string $name Name. |
| 154 | * |
| 155 | * @throws InvalidArgumentException If argument is invalid. |
| 156 | * |
| 157 | * @return $this |
| 158 | */ |
| 159 | public function set_name( $name ) { |
| 160 | if ( ! is_string( $name ) ) { |
| 161 | throw new InvalidArgumentException( '$name needs to be a string' ); |
| 162 | } |
| 163 | $this->name = $name; |
| 164 | |
| 165 | return $this; |
| 166 | } |
| 167 | |
| 168 | /** |
| 169 | * Get name. |
| 170 | * |
| 171 | * @return string |
| 172 | */ |
| 173 | public function get_name() { |
| 174 | return $this->name; |
| 175 | } |
| 176 | |
| 177 | /** |
| 178 | * Set file size. |
| 179 | * |
| 180 | * @param int $size The size. |
| 181 | * |
| 182 | * @throws InvalidArgumentException If argument is invalid. |
| 183 | * @return Tus_File |
| 184 | */ |
| 185 | public function set_file_size( $size ) { |
| 186 | $size = $this->ensure_integer( $size ); |
| 187 | $this->file_size = $size; |
| 188 | |
| 189 | return $this; |
| 190 | } |
| 191 | |
| 192 | /** |
| 193 | * Get file size. |
| 194 | * |
| 195 | * @return int |
| 196 | */ |
| 197 | public function get_file_size() { |
| 198 | return $this->file_size; |
| 199 | } |
| 200 | |
| 201 | /** |
| 202 | * Set key. |
| 203 | * |
| 204 | * @param string $key The key. |
| 205 | * |
| 206 | * @throws InvalidArgumentException If argument is invalid. |
| 207 | * @return Tus_File |
| 208 | */ |
| 209 | public function set_key( $key ) { |
| 210 | if ( ! is_string( $key ) ) { |
| 211 | throw new InvalidArgumentException( '$key needs to be a string' ); |
| 212 | } |
| 213 | $this->key = $key; |
| 214 | |
| 215 | return $this; |
| 216 | } |
| 217 | |
| 218 | /** |
| 219 | * Get key. |
| 220 | * |
| 221 | * @return string |
| 222 | */ |
| 223 | public function get_key() { |
| 224 | return $this->key; |
| 225 | } |
| 226 | |
| 227 | /** |
| 228 | * Set checksum. |
| 229 | * |
| 230 | * @param string $checksum The checksum. |
| 231 | * |
| 232 | * @throws InvalidArgumentException If argument is invalid. |
| 233 | * @return Tus_File |
| 234 | */ |
| 235 | public function set_checksum( $checksum ) { |
| 236 | if ( ! is_string( $checksum ) ) { |
| 237 | throw new InvalidArgumentException( '$checksum needs to be a string' ); |
| 238 | } |
| 239 | $this->checksum = $checksum; |
| 240 | |
| 241 | return $this; |
| 242 | } |
| 243 | |
| 244 | /** |
| 245 | * Get checksum. |
| 246 | * |
| 247 | * @return string |
| 248 | */ |
| 249 | public function get_checksum() { |
| 250 | return $this->checksum; |
| 251 | } |
| 252 | |
| 253 | /** |
| 254 | * Set offset. |
| 255 | * |
| 256 | * @param int $offset The offset. |
| 257 | * |
| 258 | * @throws InvalidArgumentException If argument is invalid. |
| 259 | * @return self |
| 260 | */ |
| 261 | public function set_offset( $offset ) { |
| 262 | $offset = $this->ensure_integer( $offset ); |
| 263 | $this->offset = absint( $offset ); |
| 264 | |
| 265 | return $this; |
| 266 | } |
| 267 | |
| 268 | /** |
| 269 | * Get offset. |
| 270 | * |
| 271 | * @return int |
| 272 | */ |
| 273 | public function get_offset() { |
| 274 | return $this->offset; |
| 275 | } |
| 276 | |
| 277 | /** |
| 278 | * Set location. |
| 279 | * |
| 280 | * @param string $location The location. |
| 281 | * |
| 282 | * @throws InvalidArgumentException If argument is invalid. |
| 283 | * @return self |
| 284 | */ |
| 285 | public function set_location( $location ) { |
| 286 | if ( ! is_string( $location ) ) { |
| 287 | throw new InvalidArgumentException( '$location needs to be a string' ); |
| 288 | } |
| 289 | $this->location = $location; |
| 290 | |
| 291 | return $this; |
| 292 | } |
| 293 | |
| 294 | /** |
| 295 | * Get location. |
| 296 | * |
| 297 | * @return string |
| 298 | */ |
| 299 | public function get_location() { |
| 300 | return $this->location; |
| 301 | } |
| 302 | |
| 303 | /** |
| 304 | * Set absolute file location. |
| 305 | * |
| 306 | * @param string $path The path. |
| 307 | * |
| 308 | * @return Tus_File |
| 309 | */ |
| 310 | public function set_file_path( $path ) { |
| 311 | $this->file_path = $path; |
| 312 | |
| 313 | return $this; |
| 314 | } |
| 315 | |
| 316 | /** |
| 317 | * Get absolute location. |
| 318 | * |
| 319 | * @return string |
| 320 | */ |
| 321 | public function get_file_path() { |
| 322 | return $this->file_path; |
| 323 | } |
| 324 | |
| 325 | /** |
| 326 | * Set the upload meta. |
| 327 | * |
| 328 | * @param array $metadata The metadata. |
| 329 | * |
| 330 | * @return Tus_File |
| 331 | */ |
| 332 | public function set_upload_metadata( array $metadata ) { |
| 333 | $this->upload_metadata = $metadata; |
| 334 | |
| 335 | return $this; |
| 336 | } |
| 337 | |
| 338 | /** |
| 339 | * Get input stream. |
| 340 | * |
| 341 | * @return string |
| 342 | */ |
| 343 | public function get_input_stream() { |
| 344 | return self::$input_stream; |
| 345 | } |
| 346 | |
| 347 | /** |
| 348 | * Set input stream. Useful for testing. |
| 349 | * |
| 350 | * @param string $stream The stream. |
| 351 | * |
| 352 | * @return void |
| 353 | */ |
| 354 | public static function set_input_stream( $stream ) { |
| 355 | self::$input_stream = $stream; |
| 356 | } |
| 357 | |
| 358 | /** |
| 359 | * Get file meta. |
| 360 | * |
| 361 | * @return array |
| 362 | * @throws \Exception If date fails. |
| 363 | */ |
| 364 | public function details() { |
| 365 | $now = Tus_Date_Utils::date_utc(); |
| 366 | $ttl = $this->cache->get_ttl(); |
| 367 | |
| 368 | return array( |
| 369 | 'name' => $this->name, |
| 370 | 'size' => $this->file_size, |
| 371 | 'offset' => $this->offset, |
| 372 | 'checksum' => $this->checksum, |
| 373 | 'location' => $this->location, |
| 374 | 'file_path' => $this->file_path, |
| 375 | 'metadata' => $this->upload_metadata, |
| 376 | 'created_at' => $now->format( Tus_Abstract_Cache::RFC_7231 ), |
| 377 | 'expires_at' => Tus_Date_Utils::add_seconds( $now, $ttl )->format( Tus_Abstract_Cache::RFC_7231 ), |
| 378 | ); |
| 379 | } |
| 380 | |
| 381 | /** |
| 382 | * Upload file to server. |
| 383 | * |
| 384 | * @param int $total_bytes The total bytes of the file. |
| 385 | * |
| 386 | * @return int |
| 387 | * @throws \Out_Of_Range_Exception Various exceptions. |
| 388 | * @throws \Connection_Exception Various exceptions. |
| 389 | * @throws File_Exception Various exceptions. |
| 390 | */ |
| 391 | public function upload( $total_bytes ) { |
| 392 | if ( $this->offset === $total_bytes ) { |
| 393 | return $this->offset; |
| 394 | } |
| 395 | |
| 396 | try { |
| 397 | $input = $this->open( $this->get_input_stream(), self::READ_BINARY ); |
| 398 | $output = $this->open( $this->get_file_path(), self::APPEND_BINARY ); |
| 399 | $key = $this->get_key(); |
| 400 | } catch ( File_Exception $fe ) { |
| 401 | Logger::log( 'error', $fe ); |
| 402 | throw new File_Exception( 'Upload failed.' ); |
| 403 | } |
| 404 | |
| 405 | try { |
| 406 | $this->seek( $output, $this->offset ); |
| 407 | |
| 408 | while ( ! feof( $input ) ) { |
| 409 | if ( CONNECTION_NORMAL !== connection_status() ) { |
| 410 | throw new \Connection_Exception( 'Connection aborted by user.' ); |
| 411 | } |
| 412 | |
| 413 | $data = $this->read( $input, self::CHUNK_SIZE ); |
| 414 | $bytes = $this->write( $output, $data, self::CHUNK_SIZE ); |
| 415 | |
| 416 | $this->offset += $bytes; |
| 417 | |
| 418 | $this->cache->set( $key, array( 'offset' => $this->offset ) ); |
| 419 | |
| 420 | if ( $this->offset > $total_bytes ) { |
| 421 | throw new \Out_Of_Range_Exception( 'The uploaded file is corrupt.' ); |
| 422 | } |
| 423 | |
| 424 | if ( $this->offset === $total_bytes ) { |
| 425 | break; |
| 426 | } |
| 427 | } |
| 428 | } finally { |
| 429 | $this->close( $input ); |
| 430 | $this->close( $output ); |
| 431 | } |
| 432 | |
| 433 | return $this->offset; |
| 434 | } |
| 435 | |
| 436 | /** |
| 437 | * Open file in given mode. |
| 438 | * |
| 439 | * @param string $file_path The file path. |
| 440 | * @param string $mode The mode. |
| 441 | * |
| 442 | * @return resource |
| 443 | * @throws File_Exception Exc. |
| 444 | * @throws InvalidArgumentException If argument is invalid. |
| 445 | */ |
| 446 | public function open( $file_path, $mode ) { |
| 447 | if ( ! is_string( $file_path ) ) { |
| 448 | throw new InvalidArgumentException( '$file_path needs to be a string' ); |
| 449 | } |
| 450 | if ( ! is_string( $mode ) ) { |
| 451 | throw new InvalidArgumentException( '$mode needs to be a string' ); |
| 452 | } |
| 453 | $this->exists( $file_path, $mode ); |
| 454 | |
| 455 | // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen, WordPress.PHP.NoSilencedErrors.Discouraged |
| 456 | $ptr = @fopen( $file_path, $mode ); |
| 457 | |
| 458 | if ( false === $ptr ) { |
| 459 | Logger::log( 'error', "Unable to open file at $file_path." ); |
| 460 | throw new File_Exception( 'Unable to open file.' ); |
| 461 | } |
| 462 | |
| 463 | return $ptr; |
| 464 | } |
| 465 | |
| 466 | /** |
| 467 | * Check if file to read exists. |
| 468 | * |
| 469 | * @param string $file_path The file path. |
| 470 | * @param string $mode The mode. |
| 471 | * |
| 472 | * @return bool |
| 473 | * @throws File_Exception File. |
| 474 | * @throws InvalidArgumentException If argument is invalid. |
| 475 | */ |
| 476 | public function exists( $file_path, $mode = self::READ_BINARY ) { |
| 477 | if ( ! is_string( $file_path ) ) { |
| 478 | throw new InvalidArgumentException( '$file_path needs to be a string' ); |
| 479 | } |
| 480 | if ( self::INPUT_STREAM === $file_path ) { |
| 481 | return true; |
| 482 | } |
| 483 | |
| 484 | if ( self::READ_BINARY === $mode && ! file_exists( $file_path ) ) { |
| 485 | throw new File_Exception( 'File not found.' ); |
| 486 | } |
| 487 | |
| 488 | return true; |
| 489 | } |
| 490 | |
| 491 | /** |
| 492 | * Move file pointer to given offset using fseek. |
| 493 | * |
| 494 | * @param resource $handle The handle. |
| 495 | * @param int $offset The offset. |
| 496 | * @param int $whence The whence. |
| 497 | * |
| 498 | * @throws File_Exception Exc. |
| 499 | * |
| 500 | * @return int |
| 501 | */ |
| 502 | public function seek( $handle, $offset, $whence = SEEK_SET ) { |
| 503 | $offset = $this->ensure_integer( $offset ); |
| 504 | $position = fseek( $handle, $offset, $whence ); |
| 505 | |
| 506 | if ( -1 === $position ) { |
| 507 | throw new File_Exception( 'Cannot move pointer to desired position.' ); |
| 508 | } |
| 509 | |
| 510 | return $position; |
| 511 | } |
| 512 | |
| 513 | /** |
| 514 | * Read data from file. |
| 515 | * |
| 516 | * @param resource $handle The handle. |
| 517 | * @param int $chunk_size Chunk size. |
| 518 | * |
| 519 | * @return string |
| 520 | * @throws File_Exception If no data is read. |
| 521 | */ |
| 522 | public function read( $handle, $chunk_size ) { |
| 523 | $chunk_size = $this->ensure_integer( $chunk_size ); |
| 524 | // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fread |
| 525 | $data = fread( $handle, $chunk_size ); |
| 526 | |
| 527 | if ( false === $data ) { |
| 528 | throw new File_Exception( 'Cannot read file.' ); |
| 529 | } |
| 530 | |
| 531 | return (string) $data; |
| 532 | } |
| 533 | |
| 534 | /** |
| 535 | * Write data to file. |
| 536 | * |
| 537 | * @param resource $handle The file handle. |
| 538 | * @param string $data The data to write. |
| 539 | * @param int|null $length Possibly the length of the data. |
| 540 | * |
| 541 | * @throws File_Exception When can't write. |
| 542 | * |
| 543 | * @return int |
| 544 | */ |
| 545 | public function write( $handle, $data, $length = null ) { |
| 546 | // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fwrite |
| 547 | $bytes_written = \is_numeric( $length ) ? fwrite( $handle, $data, intval( $length ) ) : fwrite( $handle, $data ); |
| 548 | |
| 549 | if ( false === $bytes_written ) { |
| 550 | throw new File_Exception( 'Cannot write to a file.' ); |
| 551 | } |
| 552 | |
| 553 | return $bytes_written; |
| 554 | } |
| 555 | |
| 556 | /** |
| 557 | * Merge 2 or more files. |
| 558 | * |
| 559 | * @param array $files File data with meta info. |
| 560 | * |
| 561 | * @return int |
| 562 | * @throws File_Exception When the file to be merged is not found. |
| 563 | */ |
| 564 | public function merge( array $files ) { |
| 565 | $destination = $this->get_file_path(); |
| 566 | $first_file = array_shift( $files ); |
| 567 | |
| 568 | // First partial file can directly be copied. |
| 569 | $this->copy( $first_file['file_path'], $destination ); |
| 570 | |
| 571 | $this->offset = $first_file['offset']; |
| 572 | $this->file_size = filesize( $first_file['file_path'] ); |
| 573 | |
| 574 | $handle = $this->open( $destination, self::APPEND_BINARY ); |
| 575 | |
| 576 | foreach ( $files as $file ) { |
| 577 | if ( ! file_exists( $file['file_path'] ) ) { |
| 578 | throw new File_Exception( 'File to be merged not found.' ); |
| 579 | } |
| 580 | |
| 581 | $this->file_size += $this->write( $handle, $this->get_wp_filesystem()->get_contents( $file['file_path'] ) ); |
| 582 | |
| 583 | $this->offset += $file['offset']; |
| 584 | } |
| 585 | |
| 586 | $this->close( $handle ); |
| 587 | |
| 588 | return $this->file_size; |
| 589 | } |
| 590 | |
| 591 | /** |
| 592 | * Copy file from source to destination. |
| 593 | * |
| 594 | * @param string $source The source. |
| 595 | * @param string $destination The destination. |
| 596 | * |
| 597 | * @return bool |
| 598 | * @throws File_Exception If copy fails. |
| 599 | */ |
| 600 | public function copy( $source, $destination ) { |
| 601 | $status = copy( $source, $destination ); |
| 602 | |
| 603 | if ( false === $status ) { |
| 604 | Logger::log( 'error', sprintf( 'Cannot copy source (%s) to destination (%s).', $source, $destination ) ); |
| 605 | throw new File_Exception( 'Cannot copy source file to destination file.' ); |
| 606 | } |
| 607 | |
| 608 | return $status; |
| 609 | } |
| 610 | |
| 611 | /** |
| 612 | * Delete file and/or folder. |
| 613 | * |
| 614 | * @param array $files The files. |
| 615 | * @param bool $folder The folder. |
| 616 | * |
| 617 | * @return bool |
| 618 | */ |
| 619 | public function delete( array $files, $folder = false ) { |
| 620 | $status = $this->delete_files( $files ); |
| 621 | |
| 622 | if ( $status && $folder ) { |
| 623 | return $this->get_wp_filesystem()->rmdir( \dirname( current( $files ) ) ); |
| 624 | } |
| 625 | |
| 626 | return $status; |
| 627 | } |
| 628 | |
| 629 | /** |
| 630 | * Delete multiple files. |
| 631 | * |
| 632 | * @param array $files The files. |
| 633 | * |
| 634 | * @return bool |
| 635 | */ |
| 636 | public function delete_files( array $files ) { |
| 637 | if ( empty( $files ) ) { |
| 638 | return false; |
| 639 | } |
| 640 | |
| 641 | $status = true; |
| 642 | |
| 643 | foreach ( $files as $file ) { |
| 644 | if ( $this->get_wp_filesystem()->exists( $file ) ) { |
| 645 | $r = $this->get_wp_filesystem()->delete( $file ); |
| 646 | $status = $status && $r; |
| 647 | } |
| 648 | } |
| 649 | |
| 650 | return $status; |
| 651 | } |
| 652 | |
| 653 | /** |
| 654 | * Close file. |
| 655 | * |
| 656 | * @param mixed $handle The handle. |
| 657 | * |
| 658 | * @return bool |
| 659 | */ |
| 660 | public function close( $handle ) { |
| 661 | // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose |
| 662 | return fclose( $handle ); |
| 663 | } |
| 664 | |
| 665 | /** |
| 666 | * Get the wp filesystem. |
| 667 | * |
| 668 | * @return \WP_Filesystem_Base|null |
| 669 | */ |
| 670 | private function get_wp_filesystem() { |
| 671 | global $wp_filesystem; |
| 672 | |
| 673 | if ( ! isset( $wp_filesystem ) ) { |
| 674 | require_once ABSPATH . '/wp-admin/includes/file.php'; |
| 675 | WP_Filesystem(); |
| 676 | } |
| 677 | |
| 678 | return $wp_filesystem; |
| 679 | } |
| 680 | } |