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