From 9120cf3375326e2dc8e7a6317650f8328603908b Mon Sep 17 00:00:00 2001 From: Andrew Nacin Date: Thu, 22 Nov 2012 09:52:16 +0000 Subject: [PATCH] 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 --- wp-admin/includes/image-edit.php | 4 +- wp-admin/includes/image.php | 4 +- wp-admin/includes/media.php | 4 +- wp-includes/class-wp-image-editor-gd.php | 47 ++++--- wp-includes/class-wp-image-editor-imagick.php | 53 ++++---- wp-includes/class-wp-image-editor.php | 101 ++++----------- wp-includes/deprecated.php | 47 ++++++- wp-includes/media.php | 122 +++++++++++++----- wp-settings.php | 4 - 9 files changed, 216 insertions(+), 170 deletions(-) diff --git a/wp-admin/includes/image-edit.php b/wp-admin/includes/image-edit.php index b0e95ed124..55d590278a 100644 --- a/wp-admin/includes/image-edit.php +++ b/wp-admin/includes/image-edit.php @@ -457,7 +457,7 @@ function stream_preview_image( $post_id ) { $post = get_post( $post_id ); @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 ) ) return false; @@ -566,7 +566,7 @@ function wp_save_image( $post_id ) { $success = $delete = $scaled = $nocrop = false; $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 ) ) { $return->error = esc_js( __('Unable to create new image.') ); return $return; diff --git a/wp-admin/includes/image.php b/wp-admin/includes/image.php index 053639baa5..c0fdfd9c23 100644 --- a/wp-admin/includes/image.php +++ b/wp-admin/includes/image.php @@ -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 ) ) return $editor; @@ -100,7 +100,7 @@ function wp_generate_attachment_metadata( $attachment_id, $file ) { $sizes = apply_filters( 'intermediate_image_sizes_advanced', $sizes ); if ( $sizes ) { - $editor = WP_Image_Editor::get_instance( $file ); + $editor = wp_get_image_editor( $file ); if ( ! is_wp_error( $editor ) ) $metadata['sizes'] = $editor->multi_resize( $sizes ); diff --git a/wp-admin/includes/media.php b/wp-admin/includes/media.php index 343c9fd5c8..ac55ea1387 100644 --- a/wp-admin/includes/media.php +++ b/wp-admin/includes/media.php @@ -1122,7 +1122,7 @@ function get_media_item( $attachment_id, $args = null ) { $media_dims = apply_filters( 'media_meta', $media_dims, $post ); $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" ); $image_edit_button = " "; } @@ -2253,7 +2253,7 @@ function edit_form_image_editor() { $att_url = wp_get_attachment_url( $post->ID ); $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" ); $image_edit_button = " "; } diff --git a/wp-includes/class-wp-image-editor-gd.php b/wp-includes/class-wp-image-editor-gd.php index ca76006d57..8a5d846d35 100644 --- a/wp-includes/class-wp-image-editor-gd.php +++ b/wp-includes/class-wp-image-editor-gd.php @@ -15,6 +15,7 @@ * @uses WP_Image_Editor Extends class */ class WP_Image_Editor_GD extends WP_Image_Editor { + protected $image = false; // GD Resource function __destruct() { @@ -32,13 +33,36 @@ class WP_Image_Editor_GD extends WP_Image_Editor { * * @return boolean */ - public static function test( $args = null ) { + public static function test( $args = array() ) { if ( ! extension_loaded('gd') || ! function_exists('gd_info') ) return false; 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. * @@ -47,7 +71,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor { * * @return boolean|\WP_Error */ - protected function load() { + public function load() { if ( $this->image ) return true; @@ -90,21 +114,6 @@ class WP_Image_Editor_GD extends WP_Image_Editor { 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. * 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 * @access public * - * @param boolean $horz Horizonal Flip + * @param boolean $horz Horizontal Flip * @param boolean $vert Vertical Flip * @returns boolean|WP_Error */ @@ -369,4 +378,4 @@ class WP_Image_Editor_GD extends WP_Image_Editor { return imagejpeg( $this->image, null, $this->quality ); } } -} \ No newline at end of file +} diff --git a/wp-includes/class-wp-image-editor-imagick.php b/wp-includes/class-wp-image-editor-imagick.php index 601b99b1b7..ad07ae2f93 100644 --- a/wp-includes/class-wp-image-editor-imagick.php +++ b/wp-includes/class-wp-image-editor-imagick.php @@ -15,6 +15,7 @@ * @uses WP_Image_Editor Extends class */ class WP_Image_Editor_Imagick extends WP_Image_Editor { + protected $image = null; // Imagick Object function __destruct() { @@ -36,13 +37,36 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor { * * @return boolean */ - public static function test( $args = null ) { + public static function test( $args = array() ) { if ( ! extension_loaded( 'imagick' ) || ! is_callable( 'Imagick', 'queryFormats' ) ) return false; 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. * @@ -51,7 +75,7 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor { * * @return boolean|WP_Error True if loaded; WP_Error on failure. */ - protected function load() { + public function load() { if ( $this->image ) return true; @@ -137,29 +161,6 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor { 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. * @@ -312,7 +313,7 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor { * @since 3.5.0 * @access public * - * @param boolean $horz Horizonal Flip + * @param boolean $horz Horizontal Flip * @param boolean $vert Vertical Flip * @returns boolean|WP_Error */ diff --git a/wp-includes/class-wp-image-editor.php b/wp-includes/class-wp-image-editor.php index 920e4a464b..dd4c6788ff 100644 --- a/wp-includes/class-wp-image-editor.php +++ b/wp-includes/class-wp-image-editor.php @@ -7,75 +7,51 @@ */ /** - * Base WordPress Image Editor class for which Editor implementations extend + * Base image editor class from which implementations extend * * @since 3.5.0 */ abstract class WP_Image_Editor { protected $file = null; protected $size = null; - protected $mime_type = null; + protected $mime_type = null; protected $default_mime_type = 'image/jpeg'; 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 * @access public + * @abstract * - * @param string $path Path to File to Load - * @param array $required_methods Methods to require in implementation - * @return WP_Image_Editor|WP_Error + * @param array $args + * @return boolean */ - public final static function get_instance( $path = null, $required_methods = null ) { - $implementation = apply_filters( 'wp_image_editor_class', self::choose_implementation( $required_methods ), $path ); - - 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') ); + public static function test( $args = array() ) { + return false; } /** - * 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 - * @access private + * @access public + * @abstract * - * @param array $required_methods String array of all methods required for implementation returned. - * @return string|bool Class name for the first editor that claims to support the request. False if no editor claims to support the request. + * @param string $mime_type + * @return boolean */ - private final static function choose_implementation( $required_methods = null ) { - $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; - } + public static function supports_mime_type( $mime_type ) { return false; } @@ -88,7 +64,7 @@ abstract class WP_Image_Editor { * * @return boolean|WP_Error True if loaded; WP_Error on failure. */ - abstract protected function load(); + abstract public function load(); /** * Saves current image to file. @@ -168,7 +144,7 @@ abstract class WP_Image_Editor { * @access public * @abstract * - * @param boolean $horz Horizonal Flip + * @param boolean $horz Horizontal Flip * @param boolean $vert Vertical Flip * @return boolean|WP_Error */ @@ -186,36 +162,6 @@ abstract class WP_Image_Editor { */ 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. * @@ -451,3 +397,4 @@ abstract class WP_Image_Editor { return $extensions[0]; } } + diff --git a/wp-includes/deprecated.php b/wp-includes/deprecated.php index 9e99614c49..c8fdf4ff2f 100644 --- a/wp-includes/deprecated.php +++ b/wp-includes/deprecated.php @@ -3210,13 +3210,13 @@ function _get_post_ancestors( &$post ) { * * @since 2.1.0 * @deprecated 3.5.0 - * @see WP_Image_Editor + * @see wp_get_image_editor() * * @param string $file Filename of the image to load. * @return resource The resulting image resource on success, Error string on failure. */ 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 ) ) $file = get_attached_file( $file ); @@ -3250,7 +3250,7 @@ function wp_load_image( $file ) { * * @since 2.5.0 * @deprecated 3.5.0 - * @see WP_Image_Editor + * @see wp_get_image_editor() * * @param string $file Image file path. * @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. */ 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 ) ) return $editor; $editor->set_quality( $jpeg_quality ); @@ -3328,4 +3328,39 @@ function user_pass_ok($user_login, $user_pass) { * @since 2.3.0 * @deprecated 3.5.0 */ -function _save_post_hook() {} \ No newline at end of file +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; +} diff --git a/wp-includes/media.php b/wp-includes/media.php index 9a24e68599..e96bcea0b0 100644 --- a/wp-includes/media.php +++ b/wp-includes/media.php @@ -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 ) { 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 ) ) ) return false; @@ -903,37 +903,6 @@ function get_taxonomies_for_attachments( $output = 'names' ) { 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 * @TODO: Deprecate if possible. @@ -1170,6 +1139,95 @@ function wp_max_upload_size() { 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. * diff --git a/wp-settings.php b/wp-settings.php index ac331c2cf4..65485a8410 100644 --- a/wp-settings.php +++ b/wp-settings.php @@ -143,10 +143,6 @@ require( ABSPATH . WPINC . '/nav-menu.php' ); require( ABSPATH . WPINC . '/nav-menu-template.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. if ( is_multisite() ) { require( ABSPATH . WPINC . '/ms-functions.php' );