[ 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 * @since 6.8.0 The `$dims` parameter was added. 244 * 245 * @param int $quality Compression Quality. Range: [1,100] 246 * @param array $dims Optional. Image dimensions array with 'width' and 'height' keys. 247 * @return true|WP_Error True if set successfully; WP_Error on failure. 248 249 */ 250 public function set_quality( $quality = null, $dims = array() ) { 251 // Use the output mime type if present. If not, fall back to the input/initial mime type. 252 $mime_type = ! empty( $this->output_mime_type ) ? $this->output_mime_type : $this->mime_type; 253 // Get the default quality setting for the mime type. 254 $default_quality = $this->get_default_quality( $mime_type ); 255 256 if ( null === $quality ) { 257 /** 258 * Filters the default image compression quality setting. 259 * 260 * Applies only during initial editor instantiation, or when set_quality() is run 261 * manually without the `$quality` argument. 262 * 263 * The WP_Image_Editor::set_quality() method has priority over the filter. 264 * 265 * @since 3.5.0 266 * @since 6.8.0 Added the size parameter. 267 * 268 * @param int $quality Quality level between 1 (low) and 100 (high). 269 * @param string $mime_type Image mime type. 270 * @param array $size { 271 * Dimensions of the image. 272 * 273 * @type int $width The image width. 274 * @type int $height The image height. 275 * } 276 */ 277 $quality = apply_filters( 'wp_editor_set_quality', $default_quality, $mime_type, $dims ? $dims : $this->size ); 278 279 if ( 'image/jpeg' === $mime_type ) { 280 /** 281 * Filters the JPEG compression quality for backward-compatibility. 282 * 283 * Applies only during initial editor instantiation, or when set_quality() is run 284 * manually without the `$quality` argument. 285 * 286 * The WP_Image_Editor::set_quality() method has priority over the filter. 287 * 288 * The filter is evaluated under two contexts: 'image_resize', and 'edit_image', 289 * (when a JPEG image is saved to file). 290 * 291 * @since 2.5.0 292 * 293 * @param int $quality Quality level between 0 (low) and 100 (high) of the JPEG. 294 * @param string $context Context of the filter. 295 */ 296 $quality = apply_filters( 'jpeg_quality', $quality, 'image_resize' ); 297 } 298 299 if ( $quality < 0 || $quality > 100 ) { 300 $quality = $default_quality; 301 } 302 } 303 304 // Allow 0, but squash to 1 due to identical images in GD, and for backward compatibility. 305 if ( 0 === $quality ) { 306 $quality = 1; 307 } 308 309 if ( ( $quality >= 1 ) && ( $quality <= 100 ) ) { 310 $this->quality = $quality; 311 return true; 312 } else { 313 return new WP_Error( 'invalid_image_quality', __( 'Attempted to set image quality outside of the range [1,100].' ) ); 314 } 315 } 316 317 /** 318 * Returns the default compression quality setting for the mime type. 319 * 320 * @since 5.8.1 321 * 322 * @param string $mime_type 323 * @return int The default quality setting for the mime type. 324 */ 325 protected function get_default_quality( $mime_type ) { 326 switch ( $mime_type ) { 327 case 'image/webp': 328 $quality = 86; 329 break; 330 case 'image/jpeg': 331 default: 332 $quality = $this->default_quality; 333 } 334 335 return $quality; 336 } 337 338 /** 339 * Returns preferred mime-type and extension based on provided 340 * file's extension and mime, or current file's extension and mime. 341 * 342 * Will default to $this->default_mime_type if requested is not supported. 343 * 344 * Provides corrected filename only if filename is provided. 345 * 346 * @since 3.5.0 347 * 348 * @param string $filename 349 * @param string $mime_type 350 * @return array { filename|null, extension, mime-type } 351 */ 352 protected function get_output_format( $filename = null, $mime_type = null ) { 353 $new_ext = null; 354 355 // By default, assume specified type takes priority. 356 if ( $mime_type ) { 357 $new_ext = $this->get_extension( $mime_type ); 358 } 359 360 if ( $filename ) { 361 $file_ext = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) ); 362 $file_mime = $this->get_mime_type( $file_ext ); 363 } else { 364 // If no file specified, grab editor's current extension and mime-type. 365 $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); 366 $file_mime = $this->mime_type; 367 } 368 369 /* 370 * Check to see if specified mime-type is the same as type implied by 371 * file extension. If so, prefer extension from file. 372 */ 373 if ( ! $mime_type || ( $file_mime === $mime_type ) ) { 374 $mime_type = $file_mime; 375 $new_ext = $file_ext; 376 } 377 378 $output_format = wp_get_image_editor_output_format( $filename, $mime_type ); 379 380 if ( isset( $output_format[ $mime_type ] ) 381 && $this->supports_mime_type( $output_format[ $mime_type ] ) 382 ) { 383 $mime_type = $output_format[ $mime_type ]; 384 $new_ext = $this->get_extension( $mime_type ); 385 } 386 387 /* 388 * Double-check that the mime-type selected is supported by the editor. 389 * If not, choose a default instead. 390 */ 391 if ( ! $this->supports_mime_type( $mime_type ) ) { 392 /** 393 * Filters default mime type prior to getting the file extension. 394 * 395 * @see wp_get_mime_types() 396 * 397 * @since 3.5.0 398 * 399 * @param string $mime_type Mime type string. 400 */ 401 $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type ); 402 $new_ext = $this->get_extension( $mime_type ); 403 } 404 405 /* 406 * Ensure both $filename and $new_ext are not empty. 407 * $this->get_extension() returns false on error which would effectively remove the extension 408 * from $filename. That shouldn't happen, files without extensions are not supported. 409 */ 410 if ( $filename && $new_ext ) { 411 $dir = pathinfo( $filename, PATHINFO_DIRNAME ); 412 $ext = pathinfo( $filename, PATHINFO_EXTENSION ); 413 414 $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}"; 415 } 416 417 if ( $mime_type && ( $mime_type !== $this->mime_type ) ) { 418 // The image will be converted when saving. Set the quality for the new mime-type if not already set. 419 if ( $mime_type !== $this->output_mime_type ) { 420 $this->output_mime_type = $mime_type; 421 } 422 $this->set_quality(); 423 } elseif ( ! empty( $this->output_mime_type ) ) { 424 // Reset output_mime_type and quality. 425 $this->output_mime_type = null; 426 $this->set_quality(); 427 } 428 429 return array( $filename, $new_ext, $mime_type ); 430 } 431 432 /** 433 * Builds an output filename based on current file, and adding proper suffix 434 * 435 * @since 3.5.0 436 * 437 * @param string $suffix 438 * @param string $dest_path 439 * @param string $extension 440 * @return string filename 441 */ 442 public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) { 443 // $suffix will be appended to the destination filename, just before the extension. 444 if ( ! $suffix ) { 445 $suffix = $this->get_suffix(); 446 } 447 448 $dir = pathinfo( $this->file, PATHINFO_DIRNAME ); 449 $ext = pathinfo( $this->file, PATHINFO_EXTENSION ); 450 451 $name = wp_basename( $this->file, ".$ext" ); 452 $new_ext = strtolower( $extension ? $extension : $ext ); 453 454 if ( ! is_null( $dest_path ) ) { 455 if ( ! wp_is_stream( $dest_path ) ) { 456 $_dest_path = realpath( $dest_path ); 457 if ( $_dest_path ) { 458 $dir = $_dest_path; 459 } 460 } else { 461 $dir = $dest_path; 462 } 463 } 464 465 return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}"; 466 } 467 468 /** 469 * Builds and returns proper suffix for file based on height and width. 470 * 471 * @since 3.5.0 472 * 473 * @return string|false suffix 474 */ 475 public function get_suffix() { 476 if ( ! $this->get_size() ) { 477 return false; 478 } 479 480 return "{$this->size['width']}x{$this->size['height']}"; 481 } 482 483 /** 484 * Check if a JPEG image has EXIF Orientation tag and rotate it if needed. 485 * 486 * @since 5.3.0 487 * 488 * @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). 489 * WP_Error if error while rotating. 490 */ 491 public function maybe_exif_rotate() { 492 $orientation = null; 493 494 if ( is_callable( 'exif_read_data' ) && 'image/jpeg' === $this->mime_type ) { 495 $exif_data = @exif_read_data( $this->file ); 496 497 if ( ! empty( $exif_data['Orientation'] ) ) { 498 $orientation = (int) $exif_data['Orientation']; 499 } 500 } 501 502 /** 503 * Filters the `$orientation` value to correct it before rotating or to prevent rotating the image. 504 * 505 * @since 5.3.0 506 * 507 * @param int $orientation EXIF Orientation value as retrieved from the image file. 508 * @param string $file Path to the image file. 509 */ 510 $orientation = apply_filters( 'wp_image_maybe_exif_rotate', $orientation, $this->file ); 511 512 if ( ! $orientation || 1 === $orientation ) { 513 return false; 514 } 515 516 switch ( $orientation ) { 517 case 2: 518 // Flip horizontally. 519 $result = $this->flip( false, true ); 520 break; 521 case 3: 522 /* 523 * Rotate 180 degrees or flip horizontally and vertically. 524 * Flipping seems faster and uses less resources. 525 */ 526 $result = $this->flip( true, true ); 527 break; 528 case 4: 529 // Flip vertically. 530 $result = $this->flip( true, false ); 531 break; 532 case 5: 533 // Rotate 90 degrees counter-clockwise and flip vertically. 534 $result = $this->rotate( 90 ); 535 536 if ( ! is_wp_error( $result ) ) { 537 $result = $this->flip( true, false ); 538 } 539 540 break; 541 case 6: 542 // Rotate 90 degrees clockwise (270 counter-clockwise). 543 $result = $this->rotate( 270 ); 544 break; 545 case 7: 546 // Rotate 90 degrees counter-clockwise and flip horizontally. 547 $result = $this->rotate( 90 ); 548 549 if ( ! is_wp_error( $result ) ) { 550 $result = $this->flip( false, true ); 551 } 552 553 break; 554 case 8: 555 // Rotate 90 degrees counter-clockwise. 556 $result = $this->rotate( 90 ); 557 break; 558 } 559 560 return $result; 561 } 562 563 /** 564 * Either calls editor's save function or handles file as a stream. 565 * 566 * @since 3.5.0 567 * 568 * @param string $filename 569 * @param callable $callback 570 * @param array $arguments 571 * @return bool 572 */ 573 protected function make_image( $filename, $callback, $arguments ) { 574 $stream = wp_is_stream( $filename ); 575 if ( $stream ) { 576 ob_start(); 577 } else { 578 // The directory containing the original file may no longer exist when using a replication plugin. 579 wp_mkdir_p( dirname( $filename ) ); 580 } 581 582 $result = call_user_func_array( $callback, $arguments ); 583 584 if ( $result && $stream ) { 585 $contents = ob_get_contents(); 586 587 $fp = fopen( $filename, 'w' ); 588 589 if ( ! $fp ) { 590 ob_end_clean(); 591 return false; 592 } 593 594 fwrite( $fp, $contents ); 595 fclose( $fp ); 596 } 597 598 if ( $stream ) { 599 ob_end_clean(); 600 } 601 602 return $result; 603 } 604 605 /** 606 * Returns first matched mime-type from extension, 607 * as mapped from wp_get_mime_types() 608 * 609 * @since 3.5.0 610 * 611 * @param string $extension 612 * @return string|false 613 */ 614 protected static function get_mime_type( $extension = null ) { 615 if ( ! $extension ) { 616 return false; 617 } 618 619 $mime_types = wp_get_mime_types(); 620 $extensions = array_keys( $mime_types ); 621 622 foreach ( $extensions as $_extension ) { 623 if ( preg_match( "/{$extension}/i", $_extension ) ) { 624 return $mime_types[ $_extension ]; 625 } 626 } 627 628 return false; 629 } 630 631 /** 632 * Returns first matched extension from Mime-type, 633 * as mapped from wp_get_mime_types() 634 * 635 * @since 3.5.0 636 * 637 * @param string $mime_type 638 * @return string|false 639 */ 640 protected static function get_extension( $mime_type = null ) { 641 if ( empty( $mime_type ) ) { 642 return false; 643 } 644 645 return wp_get_default_extension_for_mime_type( $mime_type ); 646 } 647 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Tue Jan 21 08:20:01 2025 | Cross-referenced by PHPXref |