[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Base WordPress Image Editor 4 * 5 * @package WordPress 6 * @subpackage Image_Editor 7 */ 8 9 /** 10 * Base image editor class from which implementations extend 11 * 12 * @since 3.5.0 13 */ 14 #[AllowDynamicProperties] 15 abstract class WP_Image_Editor { 16 protected $file = null; 17 protected $size = null; 18 protected $mime_type = null; 19 protected $output_mime_type = null; 20 protected $default_mime_type = 'image/jpeg'; 21 protected $quality = false; 22 23 // Deprecated since 5.8.1. See get_default_quality() below. 24 protected $default_quality = 82; 25 26 /** 27 * Each instance handles a single file. 28 * 29 * @param string $file Path to the file to load. 30 */ 31 public function __construct( $file ) { 32 $this->file = $file; 33 } 34 35 /** 36 * Checks to see if current environment supports the editor chosen. 37 * Must be overridden in a subclass. 38 * 39 * @since 3.5.0 40 * 41 * @abstract 42 * 43 * @param array $args 44 * @return bool 45 */ 46 public static function test( $args = array() ) { 47 return false; 48 } 49 50 /** 51 * Checks to see if editor supports the mime-type specified. 52 * Must be overridden in a subclass. 53 * 54 * @since 3.5.0 55 * 56 * @abstract 57 * 58 * @param string $mime_type 59 * @return bool 60 */ 61 public static function supports_mime_type( $mime_type ) { 62 return false; 63 } 64 65 /** 66 * Loads image from $this->file into editor. 67 * 68 * @since 3.5.0 69 * @abstract 70 * 71 * @return true|WP_Error True if loaded; WP_Error on failure. 72 */ 73 abstract public function load(); 74 75 /** 76 * Saves current image to file. 77 * 78 * @since 3.5.0 79 * @since 6.0.0 The `$filesize` value was added to the returned array. 80 * @abstract 81 * 82 * @param string $destfilename Optional. Destination filename. Default null. 83 * @param string $mime_type Optional. The mime-type. Default null. 84 * @return array|WP_Error { 85 * Array on success or WP_Error if the file failed to save. 86 * 87 * @type string $path Path to the image file. 88 * @type string $file Name of the image file. 89 * @type int $width Image width. 90 * @type int $height Image height. 91 * @type string $mime-type The mime type of the image. 92 * @type int $filesize File size of the image. 93 * } 94 */ 95 abstract public function save( $destfilename = null, $mime_type = null ); 96 97 /** 98 * Resizes current image. 99 * 100 * At minimum, either a height or width must be provided. 101 * If one of the two is set to null, the resize will 102 * maintain aspect ratio according to the provided dimension. 103 * 104 * @since 3.5.0 105 * @abstract 106 * 107 * @param int|null $max_w Image width. 108 * @param int|null $max_h Image height. 109 * @param bool|array $crop { 110 * Optional. Image cropping behavior. If false, the image will be scaled (default). 111 * If true, image will be cropped to the specified dimensions using center positions. 112 * If an array, the image will be cropped using the array to specify the crop location: 113 * 114 * @type string $0 The x crop position. Accepts 'left' 'center', or 'right'. 115 * @type string $1 The y crop position. Accepts 'top', 'center', or 'bottom'. 116 * } 117 * @return true|WP_Error 118 */ 119 abstract public function resize( $max_w, $max_h, $crop = false ); 120 121 /** 122 * Resize multiple images from a single source. 123 * 124 * @since 3.5.0 125 * @abstract 126 * 127 * @param array $sizes { 128 * An array of image size arrays. Default sizes are 'small', 'medium', 'large'. 129 * 130 * @type array ...$0 { 131 * @type int $width Image width. 132 * @type int $height Image height. 133 * @type bool|array $crop Optional. Whether to crop the image. Default false. 134 * } 135 * } 136 * @return array An array of resized images metadata by size. 137 */ 138 abstract public function multi_resize( $sizes ); 139 140 /** 141 * Crops Image. 142 * 143 * @since 3.5.0 144 * @abstract 145 * 146 * @param int $src_x The start x position to crop from. 147 * @param int $src_y The start y position to crop from. 148 * @param int $src_w The width to crop. 149 * @param int $src_h The height to crop. 150 * @param int $dst_w Optional. The destination width. 151 * @param int $dst_h Optional. The destination height. 152 * @param bool $src_abs Optional. If the source crop points are absolute. 153 * @return true|WP_Error 154 */ 155 abstract public function crop( $src_x, $src_y, $src_w, $src_h, $dst_w = null, $dst_h = null, $src_abs = false ); 156 157 /** 158 * Rotates current image counter-clockwise by $angle. 159 * 160 * @since 3.5.0 161 * @abstract 162 * 163 * @param float $angle 164 * @return true|WP_Error 165 */ 166 abstract public function rotate( $angle ); 167 168 /** 169 * Flips current image. 170 * 171 * @since 3.5.0 172 * @abstract 173 * 174 * @param bool $horz Flip along Horizontal Axis 175 * @param bool $vert Flip along Vertical Axis 176 * @return true|WP_Error 177 */ 178 abstract public function flip( $horz, $vert ); 179 180 /** 181 * Streams current image to browser. 182 * 183 * @since 3.5.0 184 * @abstract 185 * 186 * @param string $mime_type The mime type of the image. 187 * @return true|WP_Error True on success, WP_Error object on failure. 188 */ 189 abstract public function stream( $mime_type = null ); 190 191 /** 192 * Gets dimensions of image. 193 * 194 * @since 3.5.0 195 * 196 * @return int[] { 197 * Dimensions of the image. 198 * 199 * @type int $width The image width. 200 * @type int $height The image height. 201 * } 202 */ 203 public function get_size() { 204 return $this->size; 205 } 206 207 /** 208 * Sets current image size. 209 * 210 * @since 3.5.0 211 * 212 * @param int $width 213 * @param int $height 214 * @return true 215 */ 216 protected function update_size( $width = null, $height = null ) { 217 $this->size = array( 218 'width' => (int) $width, 219 'height' => (int) $height, 220 ); 221 return true; 222 } 223 224 /** 225 * Gets the Image Compression quality on a 1-100% scale. 226 * 227 * @since 4.0.0 228 * 229 * @return int Compression Quality. Range: [1,100] 230 */ 231 public function get_quality() { 232 if ( ! $this->quality ) { 233 $this->set_quality(); 234 } 235 236 return $this->quality; 237 } 238 239 /** 240 * Sets Image Compression quality on a 1-100% scale. 241 * 242 * @since 3.5.0 243 * 244 * @param int $quality Compression Quality. Range: [1,100] 245 * @return true|WP_Error True if set successfully; WP_Error on failure. 246 */ 247 public function set_quality( $quality = null ) { 248 // Use the output mime type if present. If not, fall back to the input/initial mime type. 249 $mime_type = ! empty( $this->output_mime_type ) ? $this->output_mime_type : $this->mime_type; 250 // Get the default quality setting for the mime type. 251 $default_quality = $this->get_default_quality( $mime_type ); 252 253 if ( null === $quality ) { 254 /** 255 * Filters the default image compression quality setting. 256 * 257 * Applies only during initial editor instantiation, or when set_quality() is run 258 * manually without the `$quality` argument. 259 * 260 * The WP_Image_Editor::set_quality() method has priority over the filter. 261 * 262 * @since 3.5.0 263 * 264 * @param int $quality Quality level between 1 (low) and 100 (high). 265 * @param string $mime_type Image mime type. 266 */ 267 $quality = apply_filters( 'wp_editor_set_quality', $default_quality, $mime_type ); 268 269 if ( 'image/jpeg' === $mime_type ) { 270 /** 271 * Filters the JPEG compression quality for backward-compatibility. 272 * 273 * Applies only during initial editor instantiation, or when set_quality() is run 274 * manually without the `$quality` argument. 275 * 276 * The WP_Image_Editor::set_quality() method has priority over the filter. 277 * 278 * The filter is evaluated under two contexts: 'image_resize', and 'edit_image', 279 * (when a JPEG image is saved to file). 280 * 281 * @since 2.5.0 282 * 283 * @param int $quality Quality level between 0 (low) and 100 (high) of the JPEG. 284 * @param string $context Context of the filter. 285 */ 286 $quality = apply_filters( 'jpeg_quality', $quality, 'image_resize' ); 287 } 288 289 if ( $quality < 0 || $quality > 100 ) { 290 $quality = $default_quality; 291 } 292 } 293 294 // Allow 0, but squash to 1 due to identical images in GD, and for backward compatibility. 295 if ( 0 === $quality ) { 296 $quality = 1; 297 } 298 299 if ( ( $quality >= 1 ) && ( $quality <= 100 ) ) { 300 $this->quality = $quality; 301 return true; 302 } else { 303 return new WP_Error( 'invalid_image_quality', __( 'Attempted to set image quality outside of the range [1,100].' ) ); 304 } 305 } 306 307 /** 308 * Returns the default compression quality setting for the mime type. 309 * 310 * @since 5.8.1 311 * 312 * @param string $mime_type 313 * @return int The default quality setting for the mime type. 314 */ 315 protected function get_default_quality( $mime_type ) { 316 switch ( $mime_type ) { 317 case 'image/webp': 318 $quality = 86; 319 break; 320 case 'image/jpeg': 321 case 'image/avif': 322 default: 323 $quality = $this->default_quality; 324 } 325 326 return $quality; 327 } 328 329 /** 330 * Returns preferred mime-type and extension based on provided 331 * file's extension and mime, or current file's extension and mime. 332 * 333 * Will default to $this->default_mime_type if requested is not supported. 334 * 335 * Provides corrected filename only if filename is provided. 336 * 337 * @since 3.5.0 338 * 339 * @param string $filename 340 * @param string $mime_type 341 * @return array { filename|null, extension, mime-type } 342 */ 343 protected function get_output_format( $filename = null, $mime_type = null ) { 344 $new_ext = null; 345 346 // By default, assume specified type takes priority. 347 if ( $mime_type ) { 348 $new_ext = $this->get_extension( $mime_type ); 349 } 350 351 if ( $filename ) { 352 $file_ext = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) ); 353 $file_mime = $this->get_mime_type( $file_ext ); 354 } else { 355 // If no file specified, grab editor's current extension and mime-type. 356 $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); 357 $file_mime = $this->mime_type; 358 } 359 360 /* 361 * Check to see if specified mime-type is the same as type implied by 362 * file extension. If so, prefer extension from file. 363 */ 364 if ( ! $mime_type || ( $file_mime === $mime_type ) ) { 365 $mime_type = $file_mime; 366 $new_ext = $file_ext; 367 } 368 369 /** 370 * Filters the image editor output format mapping. 371 * 372 * Enables filtering the mime type used to save images. By default, 373 * the mapping array is empty, so the mime type matches the source image. 374 * 375 * @see WP_Image_Editor::get_output_format() 376 * 377 * @since 5.8.0 378 * 379 * @param string[] $output_format { 380 * An array of mime type mappings. Maps a source mime type to a new 381 * destination mime type. Default empty array. 382 * 383 * @type string ...$0 The new mime type. 384 * } 385 * @param string $filename Path to the image. 386 * @param string $mime_type The source image mime type. 387 */ 388 $output_format = apply_filters( 'image_editor_output_format', array(), $filename, $mime_type ); 389 390 if ( isset( $output_format[ $mime_type ] ) 391 && $this->supports_mime_type( $output_format[ $mime_type ] ) 392 ) { 393 $mime_type = $output_format[ $mime_type ]; 394 $new_ext = $this->get_extension( $mime_type ); 395 } 396 397 /* 398 * Double-check that the mime-type selected is supported by the editor. 399 * If not, choose a default instead. 400 */ 401 if ( ! $this->supports_mime_type( $mime_type ) ) { 402 /** 403 * Filters default mime type prior to getting the file extension. 404 * 405 * @see wp_get_mime_types() 406 * 407 * @since 3.5.0 408 * 409 * @param string $mime_type Mime type string. 410 */ 411 $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type ); 412 $new_ext = $this->get_extension( $mime_type ); 413 } 414 415 /* 416 * Ensure both $filename and $new_ext are not empty. 417 * $this->get_extension() returns false on error which would effectively remove the extension 418 * from $filename. That shouldn't happen, files without extensions are not supported. 419 */ 420 if ( $filename && $new_ext ) { 421 $dir = pathinfo( $filename, PATHINFO_DIRNAME ); 422 $ext = pathinfo( $filename, PATHINFO_EXTENSION ); 423 424 $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}"; 425 } 426 427 if ( $mime_type && ( $mime_type !== $this->mime_type ) ) { 428 // The image will be converted when saving. Set the quality for the new mime-type if not already set. 429 if ( $mime_type !== $this->output_mime_type ) { 430 $this->output_mime_type = $mime_type; 431 } 432 $this->set_quality(); 433 } elseif ( ! empty( $this->output_mime_type ) ) { 434 // Reset output_mime_type and quality. 435 $this->output_mime_type = null; 436 $this->set_quality(); 437 } 438 439 return array( $filename, $new_ext, $mime_type ); 440 } 441 442 /** 443 * Builds an output filename based on current file, and adding proper suffix 444 * 445 * @since 3.5.0 446 * 447 * @param string $suffix 448 * @param string $dest_path 449 * @param string $extension 450 * @return string filename 451 */ 452 public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) { 453 // $suffix will be appended to the destination filename, just before the extension. 454 if ( ! $suffix ) { 455 $suffix = $this->get_suffix(); 456 } 457 458 $dir = pathinfo( $this->file, PATHINFO_DIRNAME ); 459 $ext = pathinfo( $this->file, PATHINFO_EXTENSION ); 460 461 $name = wp_basename( $this->file, ".$ext" ); 462 $new_ext = strtolower( $extension ? $extension : $ext ); 463 464 if ( ! is_null( $dest_path ) ) { 465 if ( ! wp_is_stream( $dest_path ) ) { 466 $_dest_path = realpath( $dest_path ); 467 if ( $_dest_path ) { 468 $dir = $_dest_path; 469 } 470 } else { 471 $dir = $dest_path; 472 } 473 } 474 475 return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}"; 476 } 477 478 /** 479 * Builds and returns proper suffix for file based on height and width. 480 * 481 * @since 3.5.0 482 * 483 * @return string|false suffix 484 */ 485 public function get_suffix() { 486 if ( ! $this->get_size() ) { 487 return false; 488 } 489 490 return "{$this->size['width']}x{$this->size['height']}"; 491 } 492 493 /** 494 * Check if a JPEG image has EXIF Orientation tag and rotate it if needed. 495 * 496 * @since 5.3.0 497 * 498 * @return bool|WP_Error True if the image was rotated. False if not rotated (no EXIF data or the image doesn't need to be rotated). 499 * WP_Error if error while rotating. 500 */ 501 public function maybe_exif_rotate() { 502 $orientation = null; 503 504 if ( is_callable( 'exif_read_data' ) && 'image/jpeg' === $this->mime_type ) { 505 $exif_data = @exif_read_data( $this->file ); 506 507 if ( ! empty( $exif_data['Orientation'] ) ) { 508 $orientation = (int) $exif_data['Orientation']; 509 } 510 } 511 512 /** 513 * Filters the `$orientation` value to correct it before rotating or to prevent rotating the image. 514 * 515 * @since 5.3.0 516 * 517 * @param int $orientation EXIF Orientation value as retrieved from the image file. 518 * @param string $file Path to the image file. 519 */ 520 $orientation = apply_filters( 'wp_image_maybe_exif_rotate', $orientation, $this->file ); 521 522 if ( ! $orientation || 1 === $orientation ) { 523 return false; 524 } 525 526 switch ( $orientation ) { 527 case 2: 528 // Flip horizontally. 529 $result = $this->flip( false, true ); 530 break; 531 case 3: 532 /* 533 * Rotate 180 degrees or flip horizontally and vertically. 534 * Flipping seems faster and uses less resources. 535 */ 536 $result = $this->flip( true, true ); 537 break; 538 case 4: 539 // Flip vertically. 540 $result = $this->flip( true, false ); 541 break; 542 case 5: 543 // Rotate 90 degrees counter-clockwise and flip vertically. 544 $result = $this->rotate( 90 ); 545 546 if ( ! is_wp_error( $result ) ) { 547 $result = $this->flip( true, false ); 548 } 549 550 break; 551 case 6: 552 // Rotate 90 degrees clockwise (270 counter-clockwise). 553 $result = $this->rotate( 270 ); 554 break; 555 case 7: 556 // Rotate 90 degrees counter-clockwise and flip horizontally. 557 $result = $this->rotate( 90 ); 558 559 if ( ! is_wp_error( $result ) ) { 560 $result = $this->flip( false, true ); 561 } 562 563 break; 564 case 8: 565 // Rotate 90 degrees counter-clockwise. 566 $result = $this->rotate( 90 ); 567 break; 568 } 569 570 return $result; 571 } 572 573 /** 574 * Either calls editor's save function or handles file as a stream. 575 * 576 * @since 3.5.0 577 * 578 * @param string $filename 579 * @param callable $callback 580 * @param array $arguments 581 * @return bool 582 */ 583 protected function make_image( $filename, $callback, $arguments ) { 584 $stream = wp_is_stream( $filename ); 585 if ( $stream ) { 586 ob_start(); 587 } else { 588 // The directory containing the original file may no longer exist when using a replication plugin. 589 wp_mkdir_p( dirname( $filename ) ); 590 } 591 592 $result = call_user_func_array( $callback, $arguments ); 593 594 if ( $result && $stream ) { 595 $contents = ob_get_contents(); 596 597 $fp = fopen( $filename, 'w' ); 598 599 if ( ! $fp ) { 600 ob_end_clean(); 601 return false; 602 } 603 604 fwrite( $fp, $contents ); 605 fclose( $fp ); 606 } 607 608 if ( $stream ) { 609 ob_end_clean(); 610 } 611 612 return $result; 613 } 614 615 /** 616 * Returns first matched mime-type from extension, 617 * as mapped from wp_get_mime_types() 618 * 619 * @since 3.5.0 620 * 621 * @param string $extension 622 * @return string|false 623 */ 624 protected static function get_mime_type( $extension = null ) { 625 if ( ! $extension ) { 626 return false; 627 } 628 629 $mime_types = wp_get_mime_types(); 630 $extensions = array_keys( $mime_types ); 631 632 foreach ( $extensions as $_extension ) { 633 if ( preg_match( "/{$extension}/i", $_extension ) ) { 634 return $mime_types[ $_extension ]; 635 } 636 } 637 638 return false; 639 } 640 641 /** 642 * Returns first matched extension from Mime-type, 643 * as mapped from wp_get_mime_types() 644 * 645 * @since 3.5.0 646 * 647 * @param string $mime_type 648 * @return string|false 649 */ 650 protected static function get_extension( $mime_type = null ) { 651 if ( empty( $mime_type ) ) { 652 return false; 653 } 654 655 return wp_get_default_extension_for_mime_type( $mime_type ); 656 } 657 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Fri Apr 26 08:20:02 2024 | Cross-referenced by PHPXref |