[ 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 default: 322 $quality = $this->default_quality; 323 } 324 325 return $quality; 326 } 327 328 /** 329 * Returns preferred mime-type and extension based on provided 330 * file's extension and mime, or current file's extension and mime. 331 * 332 * Will default to $this->default_mime_type if requested is not supported. 333 * 334 * Provides corrected filename only if filename is provided. 335 * 336 * @since 3.5.0 337 * 338 * @param string $filename 339 * @param string $mime_type 340 * @return array { filename|null, extension, mime-type } 341 */ 342 protected function get_output_format( $filename = null, $mime_type = null ) { 343 $new_ext = null; 344 345 // By default, assume specified type takes priority. 346 if ( $mime_type ) { 347 $new_ext = $this->get_extension( $mime_type ); 348 } 349 350 if ( $filename ) { 351 $file_ext = strtolower( pathinfo( $filename, PATHINFO_EXTENSION ) ); 352 $file_mime = $this->get_mime_type( $file_ext ); 353 } else { 354 // If no file specified, grab editor's current extension and mime-type. 355 $file_ext = strtolower( pathinfo( $this->file, PATHINFO_EXTENSION ) ); 356 $file_mime = $this->mime_type; 357 } 358 359 /* 360 * Check to see if specified mime-type is the same as type implied by 361 * file extension. If so, prefer extension from file. 362 */ 363 if ( ! $mime_type || ( $file_mime === $mime_type ) ) { 364 $mime_type = $file_mime; 365 $new_ext = $file_ext; 366 } 367 368 $output_format = wp_get_image_editor_output_format( $filename, $mime_type ); 369 370 if ( isset( $output_format[ $mime_type ] ) 371 && $this->supports_mime_type( $output_format[ $mime_type ] ) 372 ) { 373 $mime_type = $output_format[ $mime_type ]; 374 $new_ext = $this->get_extension( $mime_type ); 375 } 376 377 /* 378 * Double-check that the mime-type selected is supported by the editor. 379 * If not, choose a default instead. 380 */ 381 if ( ! $this->supports_mime_type( $mime_type ) ) { 382 /** 383 * Filters default mime type prior to getting the file extension. 384 * 385 * @see wp_get_mime_types() 386 * 387 * @since 3.5.0 388 * 389 * @param string $mime_type Mime type string. 390 */ 391 $mime_type = apply_filters( 'image_editor_default_mime_type', $this->default_mime_type ); 392 $new_ext = $this->get_extension( $mime_type ); 393 } 394 395 /* 396 * Ensure both $filename and $new_ext are not empty. 397 * $this->get_extension() returns false on error which would effectively remove the extension 398 * from $filename. That shouldn't happen, files without extensions are not supported. 399 */ 400 if ( $filename && $new_ext ) { 401 $dir = pathinfo( $filename, PATHINFO_DIRNAME ); 402 $ext = pathinfo( $filename, PATHINFO_EXTENSION ); 403 404 $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}"; 405 } 406 407 if ( $mime_type && ( $mime_type !== $this->mime_type ) ) { 408 // The image will be converted when saving. Set the quality for the new mime-type if not already set. 409 if ( $mime_type !== $this->output_mime_type ) { 410 $this->output_mime_type = $mime_type; 411 } 412 $this->set_quality(); 413 } elseif ( ! empty( $this->output_mime_type ) ) { 414 // Reset output_mime_type and quality. 415 $this->output_mime_type = null; 416 $this->set_quality(); 417 } 418 419 return array( $filename, $new_ext, $mime_type ); 420 } 421 422 /** 423 * Builds an output filename based on current file, and adding proper suffix 424 * 425 * @since 3.5.0 426 * 427 * @param string $suffix 428 * @param string $dest_path 429 * @param string $extension 430 * @return string filename 431 */ 432 public function generate_filename( $suffix = null, $dest_path = null, $extension = null ) { 433 // $suffix will be appended to the destination filename, just before the extension. 434 if ( ! $suffix ) { 435 $suffix = $this->get_suffix(); 436 } 437 438 $dir = pathinfo( $this->file, PATHINFO_DIRNAME ); 439 $ext = pathinfo( $this->file, PATHINFO_EXTENSION ); 440 441 $name = wp_basename( $this->file, ".$ext" ); 442 $new_ext = strtolower( $extension ? $extension : $ext ); 443 444 if ( ! is_null( $dest_path ) ) { 445 if ( ! wp_is_stream( $dest_path ) ) { 446 $_dest_path = realpath( $dest_path ); 447 if ( $_dest_path ) { 448 $dir = $_dest_path; 449 } 450 } else { 451 $dir = $dest_path; 452 } 453 } 454 455 return trailingslashit( $dir ) . "{$name}-{$suffix}.{$new_ext}"; 456 } 457 458 /** 459 * Builds and returns proper suffix for file based on height and width. 460 * 461 * @since 3.5.0 462 * 463 * @return string|false suffix 464 */ 465 public function get_suffix() { 466 if ( ! $this->get_size() ) { 467 return false; 468 } 469 470 return "{$this->size['width']}x{$this->size['height']}"; 471 } 472 473 /** 474 * Check if a JPEG image has EXIF Orientation tag and rotate it if needed. 475 * 476 * @since 5.3.0 477 * 478 * @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). 479 * WP_Error if error while rotating. 480 */ 481 public function maybe_exif_rotate() { 482 $orientation = null; 483 484 if ( is_callable( 'exif_read_data' ) && 'image/jpeg' === $this->mime_type ) { 485 $exif_data = @exif_read_data( $this->file ); 486 487 if ( ! empty( $exif_data['Orientation'] ) ) { 488 $orientation = (int) $exif_data['Orientation']; 489 } 490 } 491 492 /** 493 * Filters the `$orientation` value to correct it before rotating or to prevent rotating the image. 494 * 495 * @since 5.3.0 496 * 497 * @param int $orientation EXIF Orientation value as retrieved from the image file. 498 * @param string $file Path to the image file. 499 */ 500 $orientation = apply_filters( 'wp_image_maybe_exif_rotate', $orientation, $this->file ); 501 502 if ( ! $orientation || 1 === $orientation ) { 503 return false; 504 } 505 506 switch ( $orientation ) { 507 case 2: 508 // Flip horizontally. 509 $result = $this->flip( false, true ); 510 break; 511 case 3: 512 /* 513 * Rotate 180 degrees or flip horizontally and vertically. 514 * Flipping seems faster and uses less resources. 515 */ 516 $result = $this->flip( true, true ); 517 break; 518 case 4: 519 // Flip vertically. 520 $result = $this->flip( true, false ); 521 break; 522 case 5: 523 // Rotate 90 degrees counter-clockwise and flip vertically. 524 $result = $this->rotate( 90 ); 525 526 if ( ! is_wp_error( $result ) ) { 527 $result = $this->flip( true, false ); 528 } 529 530 break; 531 case 6: 532 // Rotate 90 degrees clockwise (270 counter-clockwise). 533 $result = $this->rotate( 270 ); 534 break; 535 case 7: 536 // Rotate 90 degrees counter-clockwise and flip horizontally. 537 $result = $this->rotate( 90 ); 538 539 if ( ! is_wp_error( $result ) ) { 540 $result = $this->flip( false, true ); 541 } 542 543 break; 544 case 8: 545 // Rotate 90 degrees counter-clockwise. 546 $result = $this->rotate( 90 ); 547 break; 548 } 549 550 return $result; 551 } 552 553 /** 554 * Either calls editor's save function or handles file as a stream. 555 * 556 * @since 3.5.0 557 * 558 * @param string $filename 559 * @param callable $callback 560 * @param array $arguments 561 * @return bool 562 */ 563 protected function make_image( $filename, $callback, $arguments ) { 564 $stream = wp_is_stream( $filename ); 565 if ( $stream ) { 566 ob_start(); 567 } else { 568 // The directory containing the original file may no longer exist when using a replication plugin. 569 wp_mkdir_p( dirname( $filename ) ); 570 } 571 572 $result = call_user_func_array( $callback, $arguments ); 573 574 if ( $result && $stream ) { 575 $contents = ob_get_contents(); 576 577 $fp = fopen( $filename, 'w' ); 578 579 if ( ! $fp ) { 580 ob_end_clean(); 581 return false; 582 } 583 584 fwrite( $fp, $contents ); 585 fclose( $fp ); 586 } 587 588 if ( $stream ) { 589 ob_end_clean(); 590 } 591 592 return $result; 593 } 594 595 /** 596 * Returns first matched mime-type from extension, 597 * as mapped from wp_get_mime_types() 598 * 599 * @since 3.5.0 600 * 601 * @param string $extension 602 * @return string|false 603 */ 604 protected static function get_mime_type( $extension = null ) { 605 if ( ! $extension ) { 606 return false; 607 } 608 609 $mime_types = wp_get_mime_types(); 610 $extensions = array_keys( $mime_types ); 611 612 foreach ( $extensions as $_extension ) { 613 if ( preg_match( "/{$extension}/i", $_extension ) ) { 614 return $mime_types[ $_extension ]; 615 } 616 } 617 618 return false; 619 } 620 621 /** 622 * Returns first matched extension from Mime-type, 623 * as mapped from wp_get_mime_types() 624 * 625 * @since 3.5.0 626 * 627 * @param string $mime_type 628 * @return string|false 629 */ 630 protected static function get_extension( $mime_type = null ) { 631 if ( empty( $mime_type ) ) { 632 return false; 633 } 634 635 return wp_get_default_extension_for_mime_type( $mime_type ); 636 } 637 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Thu Nov 21 08:20:01 2024 | Cross-referenced by PHPXref |