Code Modernization: Introduce `is_gd_image()` to check for PHP 8 `GdImage` object instances.

In PHP 8, the GD extension uses `GdImage` objects instead of resources for its underlying data structures.

This updates the existing `is_resource()` calls for image resources in core to accomodate for `GdImage` instances as well.

Props ayeshrajans, jrf.
Fixes #50833.

git-svn-id: https://develop.svn.wordpress.org/trunk@48798 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Sergey Biryukov 2020-08-16 13:31:57 +00:00
parent d41ac05964
commit cbaa88cb5a
7 changed files with 116 additions and 50 deletions

View File

@ -291,8 +291,8 @@ function wp_stream_image( $image, $mime_type, $attachment_id ) {
* @since 2.9.0
* @deprecated 3.5.0 Use {@see 'image_editor_save_pre'} instead.
*
* @param resource $image Image resource to be streamed.
* @param int $attachment_id The attachment post ID.
* @param resource|GdImage $image Image resource to be streamed.
* @param int $attachment_id The attachment post ID.
*/
$image = apply_filters_deprecated( 'image_save_pre', array( $image, $attachment_id ), '3.5.0', 'image_editor_save_pre' );
@ -420,19 +420,22 @@ function _image_get_preview_ratio( $w, $h ) {
* @see WP_Image_Editor::rotate()
*
* @ignore
* @param resource $img Image resource.
* @param float|int $angle Image rotation angle, in degrees.
* @return resource|false GD image resource, false otherwise.
* @param resource|GdImage $img Image resource.
* @param float|int $angle Image rotation angle, in degrees.
* @return resource|GdImage|false GD image resource or GdImage instance, false otherwise.
*/
function _rotate_image_resource( $img, $angle ) {
_deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::rotate()' );
if ( function_exists( 'imagerotate' ) ) {
$rotated = imagerotate( $img, $angle, 0 );
if ( is_resource( $rotated ) ) {
if ( is_gd_image( $rotated ) ) {
imagedestroy( $img );
$img = $rotated;
}
}
return $img;
}
@ -444,17 +447,19 @@ function _rotate_image_resource( $img, $angle ) {
* @see WP_Image_Editor::flip()
*
* @ignore
* @param resource $img Image resource.
* @param bool $horz Whether to flip horizontally.
* @param bool $vert Whether to flip vertically.
* @return resource (maybe) flipped image resource.
* @param resource|GdImage $img Image resource or GdImage instance.
* @param bool $horz Whether to flip horizontally.
* @param bool $vert Whether to flip vertically.
* @return resource|GdImage (maybe) flipped image resource or GdImage instance.
*/
function _flip_image_resource( $img, $horz, $vert ) {
_deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::flip()' );
$w = imagesx( $img );
$h = imagesy( $img );
$dst = wp_imagecreatetruecolor( $w, $h );
if ( is_resource( $dst ) ) {
if ( is_gd_image( $dst ) ) {
$sx = $vert ? ( $w - 1 ) : 0;
$sy = $horz ? ( $h - 1 ) : 0;
$sw = $vert ? -$w : $w;
@ -465,6 +470,7 @@ function _flip_image_resource( $img, $horz, $vert ) {
$img = $dst;
}
}
return $img;
}
@ -474,21 +480,23 @@ function _flip_image_resource( $img, $horz, $vert ) {
* @since 2.9.0
*
* @ignore
* @param resource $img Image resource.
* @param float $x Source point x-coordinate.
* @param float $y Source point y-coordinate.
* @param float $w Source width.
* @param float $h Source height.
* @return resource (maybe) cropped image resource.
* @param resource|GdImage $img Image resource or GdImage instance.
* @param float $x Source point x-coordinate.
* @param float $y Source point y-coordinate.
* @param float $w Source width.
* @param float $h Source height.
* @return resource|GdImage (maybe) cropped image resource or GdImage instance.
*/
function _crop_image_resource( $img, $x, $y, $w, $h ) {
$dst = wp_imagecreatetruecolor( $w, $h );
if ( is_resource( $dst ) ) {
if ( is_gd_image( $dst ) ) {
if ( imagecopy( $dst, $img, 0, 0, $x, $y, $w, $h ) ) {
imagedestroy( $img );
$img = $dst;
}
}
return $img;
}
@ -502,7 +510,7 @@ function _crop_image_resource( $img, $x, $y, $w, $h ) {
* @return WP_Image_Editor WP_Image_Editor instance with changes applied.
*/
function image_edit_apply_changes( $image, $changes ) {
if ( is_resource( $image ) ) {
if ( is_gd_image( $image ) ) {
/* translators: 1: $image, 2: WP_Image_Editor */
_deprecated_argument( __FUNCTION__, '3.5.0', sprintf( __( '%1$s needs to be a %2$s object.' ), '$image', 'WP_Image_Editor' ) );
}
@ -566,7 +574,7 @@ function image_edit_apply_changes( $image, $changes ) {
* @param array $changes Array of change operations.
*/
$image = apply_filters( 'wp_image_editor_before_change', $image, $changes );
} elseif ( is_resource( $image ) ) {
} elseif ( is_gd_image( $image ) ) {
/**
* Filters the GD image resource before applying changes to the image.
@ -574,8 +582,8 @@ function image_edit_apply_changes( $image, $changes ) {
* @since 2.9.0
* @deprecated 3.5.0 Use {@see 'wp_image_editor_before_change'} instead.
*
* @param resource $image GD image resource.
* @param array $changes Array of change operations.
* @param resource|GdImage $image GD image resource or GdImage instance.
* @param array $changes Array of change operations.
*/
$image = apply_filters_deprecated( 'image_edit_before_change', array( $image, $changes ), '3.5.0', 'wp_image_editor_before_change' );
}

View File

@ -912,15 +912,39 @@ function file_is_displayable_image( $path ) {
return apply_filters( 'file_is_displayable_image', $result, $path );
}
/**
* Determines whether the value is an acceptable type for GD image functions.
*
* In PHP 8.0, the GD extension uses GdImage objects for its data structures.
* This function checks if the passed value is either a resource of type `gd`
* or a GdImage object instance. Any other type will return false.
*
* @since 5.6.0
*
* @param resource|GdImage|false $image A value to check for the type for.
* @return bool True if $image is either a GD image resource or GdImage instance,
* false otherwise.
*/
function is_gd_image( $image ) {
if ( is_resource( $image ) && 'gd' === get_resource_type( $image )
|| is_object( $image ) && $image instanceof GdImage
) {
return true;
}
return false;
}
/**
* Load an image resource for editing.
*
* @since 2.9.0
*
* @param string $attachment_id Attachment ID.
* @param string $mime_type Image mime type.
* @param string $size Optional. Image size, defaults to 'full'.
* @return resource|false The resulting image resource on success, false on failure.
* @param string $mime_type Image mime type.
* @param string $size Optional. Image size. Default 'full'.
* @return resource|GdImage|false The resulting image resource or GdImage instance on success,
* false on failure.
*/
function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) {
$filepath = _load_image_to_edit_path( $attachment_id, $size );
@ -942,22 +966,25 @@ function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) {
$image = false;
break;
}
if ( is_resource( $image ) ) {
if ( is_gd_image( $image ) ) {
/**
* Filters the current image being loaded for editing.
*
* @since 2.9.0
*
* @param resource $image Current image.
* @param string $attachment_id Attachment ID.
* @param string $size Image size.
* @param resource|GdImage $image Current image.
* @param string $attachment_id Attachment ID.
* @param string $size Image size.
*/
$image = apply_filters( 'load_image_to_edit', $image, $attachment_id, $size );
if ( function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) {
imagealphablending( $image, false );
imagesavealpha( $image, true );
}
}
return $image;
}
@ -971,7 +998,7 @@ function load_image_to_edit( $attachment_id, $mime_type, $size = 'full' ) {
* @access private
*
* @param string $attachment_id Attachment ID.
* @param string $size Optional. Image size, defaults to 'full'.
* @param string $size Optional. Image size. Default 'full'.
* @return string|false File path or url on success, false on failure.
*/
function _load_image_to_edit_path( $attachment_id, $size = 'full' ) {

View File

@ -17,7 +17,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
/**
* GD Resource.
*
* @var resource
* @var resource|GdImage
*/
protected $image;
@ -95,7 +95,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
$this->image = @imagecreatefromstring( file_get_contents( $this->file ) );
if ( ! is_resource( $this->image ) ) {
if ( ! is_gd_image( $this->image ) ) {
return new WP_Error( 'invalid_image', __( 'File is not an image.' ), $this->file );
}
@ -138,11 +138,11 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
/**
* Resizes current image.
* Wraps _resize, since _resize returns a GD Resource.
*
* At minimum, either a height or width must be provided.
* If one of the two is set to null, the resize will
* maintain aspect ratio according to the provided dimension.
* Wraps `::_resize()` which returns a GD resource or GdImage instance.
*
* At minimum, either a height or width must be provided. If one of the two is set
* to null, the resize will maintain aspect ratio according to the provided dimension.
*
* @since 3.5.0
*
@ -158,7 +158,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
$resized = $this->_resize( $max_w, $max_h, $crop );
if ( is_resource( $resized ) ) {
if ( is_gd_image( $resized ) ) {
imagedestroy( $this->image );
$this->image = $resized;
return true;
@ -174,7 +174,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
* @param int $max_w
* @param int $max_h
* @param bool|array $crop
* @return resource|WP_Error
* @return resource|GdImage|WP_Error
*/
protected function _resize( $max_w, $max_h, $crop = false ) {
$dims = image_resize_dimensions( $this->size['width'], $this->size['height'], $max_w, $max_h, $crop );
@ -188,7 +188,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
$resized = wp_imagecreatetruecolor( $dst_w, $dst_h );
imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
if ( is_resource( $resized ) ) {
if ( is_gd_image( $resized ) ) {
$this->update_size( $dst_w, $dst_h );
return $resized;
}
@ -329,7 +329,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
imagecopyresampled( $dst, $this->image, 0, 0, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h );
if ( is_resource( $dst ) ) {
if ( is_gd_image( $dst ) ) {
imagedestroy( $this->image );
$this->image = $dst;
$this->update_size();
@ -353,7 +353,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
$transparency = imagecolorallocatealpha( $this->image, 255, 255, 255, 127 );
$rotated = imagerotate( $this->image, $angle, $transparency );
if ( is_resource( $rotated ) ) {
if ( is_gd_image( $rotated ) ) {
imagealphablending( $rotated, true );
imagesavealpha( $rotated, true );
imagedestroy( $this->image );
@ -362,6 +362,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
return true;
}
}
return new WP_Error( 'image_rotate_error', __( 'Image rotate failed.' ), $this->file );
}
@ -379,7 +380,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
$h = $this->size['height'];
$dst = wp_imagecreatetruecolor( $w, $h );
if ( is_resource( $dst ) ) {
if ( is_gd_image( $dst ) ) {
$sx = $vert ? ( $w - 1 ) : 0;
$sy = $horz ? ( $h - 1 ) : 0;
$sw = $vert ? -$w : $w;
@ -391,6 +392,7 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
return true;
}
}
return new WP_Error( 'image_flip_error', __( 'Image flip failed.' ), $this->file );
}
@ -415,9 +417,9 @@ class WP_Image_Editor_GD extends WP_Image_Editor {
}
/**
* @param resource $image
* @param string|null $filename
* @param string|null $mime_type
* @param resource|GdImage $image
* @param string|null $filename
* @param string|null $mime_type
* @return array|WP_Error
*/
protected function _save( $image, $filename = null, $mime_type = null ) {

View File

@ -548,6 +548,7 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
} catch ( Exception $e ) {
return new WP_Error( 'image_crop_error', $e->getMessage() );
}
return $this->update_size();
}
@ -582,6 +583,7 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
} catch ( Exception $e ) {
return new WP_Error( 'image_rotate_error', $e->getMessage() );
}
return true;
}

View File

@ -3196,7 +3196,8 @@ function _get_post_ancestors( &$post ) {
* @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.
* @return resource|GdImage|string The resulting image resource or GdImage instance on success,
* error string on failure.
*/
function wp_load_image( $file ) {
_deprecated_function( __FUNCTION__, '3.5.0', 'wp_get_image_editor()' );
@ -3217,7 +3218,7 @@ function wp_load_image( $file ) {
$image = imagecreatefromstring( file_get_contents( $file ) );
if ( ! is_resource( $image ) ) {
if ( ! is_gd_image( $image ) ) {
/* translators: %s: File name. */
return sprintf( __( 'File “%s” is not an image.' ), $file );
}

View File

@ -3459,15 +3459,19 @@ function get_taxonomies_for_attachments( $output = 'names' ) {
* @since 2.9.0
*
* @param int $width Image width in pixels.
* @param int $height Image height in pixels..
* @return resource The GD image resource.
* @param int $height Image height in pixels.
* @return resource|GdImage The GD image resource or GdImage instance.
*/
function wp_imagecreatetruecolor( $width, $height ) {
$img = imagecreatetruecolor( $width, $height );
if ( is_resource( $img ) && function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' ) ) {
if ( is_gd_image( $img )
&& function_exists( 'imagealphablending' ) && function_exists( 'imagesavealpha' )
) {
imagealphablending( $img, false );
imagesavealpha( $img, true );
}
return $img;
}

View File

@ -123,6 +123,28 @@ class Tests_Image_Functions extends WP_UnitTestCase {
}
}
/**
* @ticket 50833
*/
function test_is_gd_image_invalid_types() {
$this->assertFalse( is_gd_image( new stdClass() ) );
$this->assertFalse( is_gd_image( array() ) );
$this->assertFalse( is_gd_image( null ) );
$handle = fopen( __FILE__, 'r' );
$this->assertFalse( is_gd_image( $handle ) );
fclose( $handle );
}
/**
* @ticket 50833
* @requires extension gd
*/
function test_is_gd_image_valid_types() {
$this->assertTrue( is_gd_image( imagecreate( 5, 5 ) ) );
}
/**
* Test save image file and mime_types
*