WP_Image_Editor: the last stand.

* Have wp_get_image_editor() rather than WP_Image_Editor::get_instance(). Having static factory methods would be less confusing if there weren't also static methods tied to individual editor implementations.
 * Lazy-load the WP_Image_Editor base class and editor implementations.
 * Have WP_Image_Editor_GD::supports_mime_type() actually check which types it supports.
 * Deprecate gd_edit_image_support() in favor of wp_image_editor_supports().

props DH-Shredder, scribu, markoheijnen. fixes #22356. see #6821.



git-svn-id: http://core.svn.wordpress.org/trunk@22817 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Andrew Nacin 2012-11-22 09:52:16 +00:00
parent 1e472d8974
commit 9120cf3375
9 changed files with 216 additions and 170 deletions

View File

@ -457,7 +457,7 @@ function stream_preview_image( $post_id ) {
$post = get_post( $post_id ); $post = get_post( $post_id );
@ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) ); @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
$img = WP_Image_Editor::get_instance( _load_image_to_edit_path( $post_id ) ); $img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) );
if ( is_wp_error( $img ) ) if ( is_wp_error( $img ) )
return false; return false;
@ -566,7 +566,7 @@ function wp_save_image( $post_id ) {
$success = $delete = $scaled = $nocrop = false; $success = $delete = $scaled = $nocrop = false;
$post = get_post( $post_id ); $post = get_post( $post_id );
$img = WP_Image_Editor::get_instance( _load_image_to_edit_path( $post_id, 'full' ) ); $img = wp_get_image_editor( _load_image_to_edit_path( $post_id, 'full' ) );
if ( is_wp_error( $img ) ) { if ( is_wp_error( $img ) ) {
$return->error = esc_js( __('Unable to create new image.') ); $return->error = esc_js( __('Unable to create new image.') );
return $return; return $return;

View File

@ -36,7 +36,7 @@ function wp_crop_image( $src, $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h, $s
} }
} }
$editor = WP_Image_Editor::get_instance( $src ); $editor = wp_get_image_editor( $src );
if ( is_wp_error( $editor ) ) if ( is_wp_error( $editor ) )
return $editor; return $editor;
@ -100,7 +100,7 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) {
$sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes ); $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes );
if ( $sizes ) { if ( $sizes ) {
$editor = WP_Image_Editor::get_instance( $file ); $editor = wp_get_image_editor( $file );
if ( ! is_wp_error( $editor ) ) if ( ! is_wp_error( $editor ) )
$metadata['sizes'] = $editor->multi_resize( $sizes ); $metadata['sizes'] = $editor->multi_resize( $sizes );

View File

@ -1122,7 +1122,7 @@ function get_media_item( $attachment_id, $args = null ) {
$media_dims = apply_filters( 'media_meta', $media_dims, $post ); $media_dims = apply_filters( 'media_meta', $media_dims, $post );
$image_edit_button = ''; $image_edit_button = '';
if ( gd_edit_image_support( $post->post_mime_type ) ) { if ( wp_image_editor_supports( array( 'mime_type' => $post->post_mime_type ) ) ) {
$nonce = wp_create_nonce( "image_editor-$post->ID" ); $nonce = wp_create_nonce( "image_editor-$post->ID" );
$image_edit_button = "<input type='button' id='imgedit-open-btn-$post->ID' onclick='imageEdit.open( $post->ID, \"$nonce\" )' class='button' value='" . esc_attr__( 'Edit Image' ) . "' /> <span class='spinner'></span>"; $image_edit_button = "<input type='button' id='imgedit-open-btn-$post->ID' onclick='imageEdit.open( $post->ID, \"$nonce\" )' class='button' value='" . esc_attr__( 'Edit Image' ) . "' /> <span class='spinner'></span>";
} }
@ -2253,7 +2253,7 @@ function edit_form_image_editor() {
$att_url = wp_get_attachment_url( $post->ID ); $att_url = wp_get_attachment_url( $post->ID );
$image_edit_button = ''; $image_edit_button = '';
if ( gd_edit_image_support( $post->post_mime_type ) ) { if ( wp_image_editor_supports( array( 'mime_type' => $post->post_mime_type ) ) ) {
$nonce = wp_create_nonce( "image_editor-$post->ID" ); $nonce = wp_create_nonce( "image_editor-$post->ID" );
$image_edit_button = "<input type='button' id='imgedit-open-btn-$post->ID' onclick='imageEdit.open( $post->ID, \"$nonce\" )' class='button' value='" . esc_attr__( 'Edit Image' ) . "' /> <span class='spinner'></span>"; $image_edit_button = "<input type='button' id='imgedit-open-btn-$post->ID' onclick='imageEdit.open( $post->ID, \"$nonce\" )' class='button' value='" . esc_attr__( 'Edit Image' ) . "' /> <span class='spinner'></span>";
} }

View File

@ -15,6 +15,7 @@
* @uses WP_Image_Editor Extends class * @uses WP_Image_Editor Extends class
*/ */
class WP_Image_Editor_GD extends WP_Image_Editor { class WP_Image_Editor_GD extends WP_Image_Editor {
protected $image = false; // GD Resource protected $image = false; // GD Resource
function __destruct() { function __destruct() {
@ -32,13 +33,36 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
* *
* @return boolean * @return boolean
*/ */
public static function test( $args = null ) { public static function test( $args = array() ) {
if ( ! extension_loaded('gd') || ! function_exists('gd_info') ) if ( ! extension_loaded('gd') || ! function_exists('gd_info') )
return false; return false;
return true; return true;
} }
/**
* Checks to see if editor supports the mime-type specified.
*
* @since 3.5.0
* @access public
*
* @param string $mime_type
* @return boolean
*/
public static function supports_mime_type( $mime_type ) {
$image_types = imagetypes();
switch( $mime_type ) {
case 'image/jpeg':
return ($image_types & IMG_JPG) != 0;
case 'image/png':
return ($image_types & IMG_PNG) != 0;
case 'image/gif':
return ($image_types & IMG_GIF) != 0;
}
return false;
}
/** /**
* Loads image from $this->file into new GD Resource. * Loads image from $this->file into new GD Resource.
* *
@ -47,7 +71,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
* *
* @return boolean|\WP_Error * @return boolean|\WP_Error
*/ */
protected function load() { public function load() {
if ( $this->image ) if ( $this->image )
return true; return true;
@ -90,21 +114,6 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
return parent::update_size( $width, $height ); return parent::update_size( $width, $height );
} }
/**
* Checks to see if editor supports the mime-type specified.
*
* @since 3.5.0
* @access public
*
* @param string $mime_type
* @return boolean
*/
public static function supports_mime_type( $mime_type ) {
$allowed_mime_types = array( 'image/gif', 'image/png', 'image/jpeg' );
return in_array( $mime_type, $allowed_mime_types );
}
/** /**
* Resizes current image. * Resizes current image.
* Wraps _resize, since _resize returns a GD Resource. * Wraps _resize, since _resize returns a GD Resource.
@ -261,7 +270,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
* @since 3.5.0 * @since 3.5.0
* @access public * @access public
* *
* @param boolean $horz Horizonal Flip * @param boolean $horz Horizontal Flip
* @param boolean $vert Vertical Flip * @param boolean $vert Vertical Flip
* @returns boolean|WP_Error * @returns boolean|WP_Error
*/ */

View File

@ -15,6 +15,7 @@
* @uses WP_Image_Editor Extends class * @uses WP_Image_Editor Extends class
*/ */
class WP_Image_Editor_Imagick extends WP_Image_Editor { class WP_Image_Editor_Imagick extends WP_Image_Editor {
protected $image = null; // Imagick Object protected $image = null; // Imagick Object
function __destruct() { function __destruct() {
@ -36,13 +37,36 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
* *
* @return boolean * @return boolean
*/ */
public static function test( $args = null ) { public static function test( $args = array() ) {
if ( ! extension_loaded( 'imagick' ) || ! is_callable( 'Imagick', 'queryFormats' ) ) if ( ! extension_loaded( 'imagick' ) || ! is_callable( 'Imagick', 'queryFormats' ) )
return false; return false;
return true; return true;
} }
/**
* Checks to see if editor supports the mime-type specified.
*
* @since 3.5.0
* @access public
*
* @param string $mime_type
* @return boolean
*/
public static function supports_mime_type( $mime_type ) {
$imagick_extension = strtoupper( self::get_extension( $mime_type ) );
if ( ! $imagick_extension )
return false;
try {
return ( (bool) Imagick::queryFormats( $imagick_extension ) );
}
catch ( Exception $e ) {
return false;
}
}
/** /**
* Loads image from $this->file into new Imagick Object. * Loads image from $this->file into new Imagick Object.
* *
@ -51,7 +75,7 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
* *
* @return boolean|WP_Error True if loaded; WP_Error on failure. * @return boolean|WP_Error True if loaded; WP_Error on failure.
*/ */
protected function load() { public function load() {
if ( $this->image ) if ( $this->image )
return true; return true;
@ -137,29 +161,6 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
return parent::update_size( $width, $height ); return parent::update_size( $width, $height );
} }
/**
* Checks to see if editor supports the mime-type specified.
*
* @since 3.5.0
* @access public
*
* @param string $mime_type
* @return boolean
*/
public static function supports_mime_type( $mime_type ) {
if ( ! $mime_type )
return false;
$imagick_extension = strtoupper( self::get_extension( $mime_type ) );
try {
return ( (bool) Imagick::queryFormats( $imagick_extension ) );
}
catch ( Exception $e ) {
return false;
}
}
/** /**
* Resizes current image. * Resizes current image.
* *
@ -312,7 +313,7 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
* @since 3.5.0 * @since 3.5.0
* @access public * @access public
* *
* @param boolean $horz Horizonal Flip * @param boolean $horz Horizontal Flip
* @param boolean $vert Vertical Flip * @param boolean $vert Vertical Flip
* @returns boolean|WP_Error * @returns boolean|WP_Error
*/ */

View File

@ -7,7 +7,7 @@
*/ */
/** /**
* Base WordPress Image Editor class for which Editor implementations extend * Base image editor class from which implementations extend
* *
* @since 3.5.0 * @since 3.5.0
*/ */
@ -18,64 +18,40 @@ abstract class WP_Image_Editor {
protected $default_mime_type = 'image/jpeg'; protected $default_mime_type = 'image/jpeg';
protected $quality = 90; protected $quality = 90;
protected function __construct( $filename ) { /**
$this->file = $filename; * Each instance handles a single file.
*/
public function __construct( $file ) {
$this->file = $file;
} }
/** /**
* Returns a WP_Image_Editor instance and loads file into it. * Checks to see if current environment supports the editor chosen.
* Must be overridden in a sub-class.
* *
* @since 3.5.0 * @since 3.5.0
* @access public * @access public
* @abstract
* *
* @param string $path Path to File to Load * @param array $args
* @param array $required_methods Methods to require in implementation * @return boolean
* @return WP_Image_Editor|WP_Error
*/ */
public final static function get_instance( $path = null, $required_methods = null ) { public static function test( $args = array() ) {
$implementation = apply_filters( 'wp_image_editor_class', self::choose_implementation( $required_methods ), $path ); return false;
if ( $implementation ) {
$editor = new $implementation( $path );
$loaded = $editor->load();
if ( is_wp_error( $loaded ) )
return $loaded;
return $editor;
}
return new WP_Error( 'no_editor', __('No editor could be selected') );
} }
/** /**
* Tests which editors are capable of supporting the request. * Checks to see if editor supports the mime-type specified.
* Must be overridden in a sub-class.
* *
* @since 3.5.0 * @since 3.5.0
* @access private * @access public
* @abstract
* *
* @param array $required_methods String array of all methods required for implementation returned. * @param string $mime_type
* @return string|bool Class name for the first editor that claims to support the request. False if no editor claims to support the request. * @return boolean
*/ */
private final static function choose_implementation( $required_methods = null ) { public static function supports_mime_type( $mime_type ) {
$request_order = apply_filters( 'wp_image_editors',
array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
if ( ! $required_methods )
$required_methods = array();
// Loop over each editor on each request looking for one which will serve this request's needs
foreach ( $request_order as $editor ) {
// Check to see if this editor is a possibility, calls the editor statically
if ( ! call_user_func( array( $editor, 'test' ) ) )
continue;
// Make sure that all methods are supported by editor.
if ( array_diff( $required_methods, get_class_methods( $editor ) ) )
continue;
return $editor;
}
return false; return false;
} }
@ -88,7 +64,7 @@ abstract class WP_Image_Editor {
* *
* @return boolean|WP_Error True if loaded; WP_Error on failure. * @return boolean|WP_Error True if loaded; WP_Error on failure.
*/ */
abstract protected function load(); abstract public function load();
/** /**
* Saves current image to file. * Saves current image to file.
@ -168,7 +144,7 @@ abstract class WP_Image_Editor {
* @access public * @access public
* @abstract * @abstract
* *
* @param boolean $horz Horizonal Flip * @param boolean $horz Horizontal Flip
* @param boolean $vert Vertical Flip * @param boolean $vert Vertical Flip
* @return boolean|WP_Error * @return boolean|WP_Error
*/ */
@ -186,36 +162,6 @@ abstract class WP_Image_Editor {
*/ */
abstract public function stream( $mime_type = null ); abstract public function stream( $mime_type = null );
/**
* Checks to see if current environment supports the editor chosen.
* Must be overridden in a sub-class.
*
* @since 3.5.0
* @access public
* @abstract
*
* @param array $args
* @return boolean
*/
public static function test( $args = null ) {
return false;
}
/**
* Checks to see if editor supports the mime-type specified.
* Must be overridden in a sub-class.
*
* @since 3.5.0
* @access public
* @abstract
*
* @param string $mime_type
* @return boolean
*/
public static function supports_mime_type( $mime_type ) {
return false;
}
/** /**
* Gets dimensions of image. * Gets dimensions of image.
* *
@ -451,3 +397,4 @@ abstract class WP_Image_Editor {
return $extensions[0]; return $extensions[0];
} }
} }

View File

@ -3210,13 +3210,13 @@ function _get_post_ancestors( &$post ) {
* *
* @since 2.1.0 * @since 2.1.0
* @deprecated 3.5.0 * @deprecated 3.5.0
* @see WP_Image_Editor * @see wp_get_image_editor()
* *
* @param string $file Filename of the image to load. * @param string $file Filename of the image to load.
* @return resource The resulting image resource on success, Error string on failure. * @return resource The resulting image resource on success, Error string on failure.
*/ */
function wp_load_image( $file ) { function wp_load_image( $file ) {
_deprecated_function( __FUNCTION__, '3.5', 'WP_Image_Editor' ); _deprecated_function( __FUNCTION__, '3.5', 'wp_get_image_editor()' );
if ( is_numeric( $file ) ) if ( is_numeric( $file ) )
$file = get_attached_file( $file ); $file = get_attached_file( $file );
@ -3250,7 +3250,7 @@ function wp_load_image( $file ) {
* *
* @since 2.5.0 * @since 2.5.0
* @deprecated 3.5.0 * @deprecated 3.5.0
* @see WP_Image_Editor * @see wp_get_image_editor()
* *
* @param string $file Image file path. * @param string $file Image file path.
* @param int $max_w Maximum width to resize to. * @param int $max_w Maximum width to resize to.
@ -3262,9 +3262,9 @@ function wp_load_image( $file ) {
* @return mixed WP_Error on failure. String with new destination path. * @return mixed WP_Error on failure. String with new destination path.
*/ */
function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) { function image_resize( $file, $max_w, $max_h, $crop = false, $suffix = null, $dest_path = null, $jpeg_quality = 90 ) {
_deprecated_function( __FUNCTION__, '3.5', 'WP_Image_Editor' ); _deprecated_function( __FUNCTION__, '3.5', 'wp_get_image_editor()' );
$editor = WP_Image_Editor::get_instance( $file ); $editor = wp_get_image_editor( $file );
if ( is_wp_error( $editor ) ) if ( is_wp_error( $editor ) )
return $editor; return $editor;
$editor->set_quality( $jpeg_quality ); $editor->set_quality( $jpeg_quality );
@ -3329,3 +3329,38 @@ function user_pass_ok($user_login, $user_pass) {
* @deprecated 3.5.0 * @deprecated 3.5.0
*/ */
function _save_post_hook() {} function _save_post_hook() {}
/**
* Check if the installed version of GD supports particular image type
*
* @since 2.9.0
* @deprecated 3.5.0
* see wp_image_editor_supports()
*
* @param string $mime_type
* @return bool
*/
function gd_edit_image_support($mime_type) {
_deprecated_function( __FUNCTION__, '3.5', 'wp_image_editor_supports()' );
if ( function_exists('imagetypes') ) {
switch( $mime_type ) {
case 'image/jpeg':
return (imagetypes() & IMG_JPG) != 0;
case 'image/png':
return (imagetypes() & IMG_PNG) != 0;
case 'image/gif':
return (imagetypes() & IMG_GIF) != 0;
}
} else {
switch( $mime_type ) {
case 'image/jpeg':
return function_exists('imagecreatefromjpeg');
case 'image/png':
return function_exists('imagecreatefrompng');
case 'image/gif':
return function_exists('imagecreatefromgif');
}
}
return false;
}

View File

@ -383,7 +383,7 @@ function image_resize_dimensions($orig_w, $orig_h, $dest_w, $dest_h, $crop = fal
*/ */
function image_make_intermediate_size( $file, $width, $height, $crop = false ) { function image_make_intermediate_size( $file, $width, $height, $crop = false ) {
if ( $width || $height ) { if ( $width || $height ) {
$editor = WP_Image_Editor::get_instance( $file ); $editor = wp_get_image_editor( $file );
if ( is_wp_error( $editor ) || is_wp_error( $editor->resize( $width, $height, $crop ) ) ) if ( is_wp_error( $editor ) || is_wp_error( $editor->resize( $width, $height, $crop ) ) )
return false; return false;
@ -903,37 +903,6 @@ function get_taxonomies_for_attachments( $output = 'names' ) {
return $taxonomies; return $taxonomies;
} }
/**
* Check if the installed version of GD supports particular image type
*
* @since 2.9.0
*
* @param string $mime_type
* @return bool
*/
function gd_edit_image_support($mime_type) {
if ( function_exists('imagetypes') ) {
switch( $mime_type ) {
case 'image/jpeg':
return (imagetypes() & IMG_JPG) != 0;
case 'image/png':
return (imagetypes() & IMG_PNG) != 0;
case 'image/gif':
return (imagetypes() & IMG_GIF) != 0;
}
} else {
switch( $mime_type ) {
case 'image/jpeg':
return function_exists('imagecreatefromjpeg');
case 'image/png':
return function_exists('imagecreatefrompng');
case 'image/gif':
return function_exists('imagecreatefromgif');
}
}
return false;
}
/** /**
* Create new GD image resource with transparency support * Create new GD image resource with transparency support
* @TODO: Deprecate if possible. * @TODO: Deprecate if possible.
@ -1170,6 +1139,95 @@ function wp_max_upload_size() {
return $bytes; return $bytes;
} }
/**
* Returns a WP_Image_Editor instance and loads file into it.
*
* @since 3.5.0
* @access public
*
* @param string $path Path to file to load
* @param array $args Additional data. Accepts { 'mime_type'=>string, 'methods'=>{string, string, ...} }
* @return WP_Image_Editor|WP_Error
*/
function wp_get_image_editor( $path, $args = array() ) {
$args['path'] = $path;
if ( ! isset( $args['mime_type'] ) ) {
$file_info = wp_check_filetype( $args['path'] );
// If $file_info['type'] is false, then we let the editor attempt to
// figure out the file type, rather than forcing a failure based on extension.
if ( isset( $file_info ) && $file_info['type'] )
$args['mime_type'] = $file_info['type'];
}
$implementation = apply_filters( 'wp_image_editor_class', _wp_image_editor_choose( $args ) );
if ( $implementation ) {
$editor = new $implementation( $path );
$loaded = $editor->load();
if ( is_wp_error( $loaded ) )
return $loaded;
return $editor;
}
return new WP_Error( 'image_no_editor', __('No editor could be selected.') );
}
/**
* Tests whether there is an editor that supports a given mime type or methods.
*
* @since 3.5.0
* @access public
*
* @param string|array $args Array of requirements. Accepts { 'mime_type'=>string, 'methods'=>{string, string, ...} }
* @return boolean true if an eligible editor is found; false otherwise
*/
function wp_image_editor_supports( $args = array() ) {
return (bool) _wp_image_editor_choose( $args );
}
/**
* Tests which editors are capable of supporting the request.
*
* @since 3.5.0
* @access private
*
* @param array $args Additional data. Accepts { 'mime_type'=>string, 'methods'=>{string, string, ...} }
* @return string|bool Class name for the first editor that claims to support the request. False if no editor claims to support the request.
*/
function _wp_image_editor_choose( $args = array() ) {
require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-gd.php';
require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
$implementations = apply_filters( 'wp_image_editors',
array( 'WP_Image_Editor_Imagick', 'WP_Image_Editor_GD' ) );
foreach ( $implementations as $implementation ) {
if ( ! call_user_func( array( $implementation, 'test' ), $args ) )
continue;
if ( isset( $args['mime_type'] ) &&
! call_user_func(
array( $implementation, 'supports_mime_type' ),
$args['mime_type'] ) ) {
continue;
}
if ( isset( $args['methods'] ) &&
array_diff( $args['methods'], get_class_methods( $implementation ) ) ) {
continue;
}
return $implementation;
}
return false;
}
/** /**
* Prints default plupload arguments. * Prints default plupload arguments.
* *

View File

@ -143,10 +143,6 @@ require( ABSPATH . WPINC . '/nav-menu.php' );
require( ABSPATH . WPINC . '/nav-menu-template.php' ); require( ABSPATH . WPINC . '/nav-menu-template.php' );
require( ABSPATH . WPINC . '/admin-bar.php' ); require( ABSPATH . WPINC . '/admin-bar.php' );
require( ABSPATH . WPINC . '/class-wp-image-editor.php' );
require( ABSPATH . WPINC . '/class-wp-image-editor-gd.php' );
require( ABSPATH . WPINC . '/class-wp-image-editor-imagick.php' );
// Load multisite-specific files. // Load multisite-specific files.
if ( is_multisite() ) { if ( is_multisite() ) {
require( ABSPATH . WPINC . '/ms-functions.php' ); require( ABSPATH . WPINC . '/ms-functions.php' );