Make WordPress Core

Changeset 49230


Ignore:
Timestamp:
10/20/2020 02:35:10 PM (4 years ago)
Author:
mikeschroder
Message:

Media: Support Stream Wrappers In WP_Image_Editor_Imagick

Since WP_Image_Editor's introduction, stream wrappers have functioned in WP_Image_Editor_GD, but haven't been properly supported in WP_Image_Editor_Imagick.

  • Detects stream wrappers and uses file_put_contents() along with Imagick::read/getImageBlob() for handling when necessary.
  • Introduces private method, WP_Image_Editor_Imagick::write_image to handle detection and proper saving.
  • Introduces WP_Test_Stream class for testing stream wrappers, along with new tests for Imagick's stream handling and a stream filename test.

Adds requirement for Imagick::readImageBlob(), available in Imagick >= 2.0.0, which aligns with the current requirement of Imagick >= 2.2.0.

Props p00ya, calin, joemcgill, pputzer, jimyaghi, mikeschroder.
Fixes #42663.

Location:
trunk
Files:
1 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-image-editor-imagick.php

    r49174 r49230  
    7272            'flopimage',
    7373            'readimage',
     74            'readimageblob',
    7475        );
    7576
     
    128129        }
    129130
    130         if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) ) {
     131        if ( ! is_file( $this->file ) && ! wp_is_stream( $this->file ) ) {
    131132            return new WP_Error( 'error_loading_image', __( 'File doesn’t exist?' ), $this->file );
    132133        }
     
    149150                }
    150151            } else {
    151                 $this->image->readImage( $this->file );
     152                if ( wp_is_stream( $this->file ) ) {
     153                    // Due to reports of issues with streams with `Imagick::readImageFile()`, uses `Imagick::readImageBlob()` instead.
     154                    $this->image->readImageBlob( file_get_contents( $this->file ), $this->file );
     155                } else {
     156                    $this->image->readImage( $this->file );
     157                }
    152158            }
    153159
     
    683689
    684690            $this->image->setImageFormat( strtoupper( $this->get_extension( $mime_type ) ) );
    685             $this->make_image( $filename, array( $image, 'writeImage' ), array( $filename ) );
    686 
     691        } catch ( Exception $e ) {
     692            return new WP_Error( 'image_save_error', $e->getMessage(), $filename );
     693        }
     694
     695        $write_image_result = $this->write_image( $this->image, $filename );
     696        if ( is_wp_error( $write_image_result ) ) {
     697            return $write_image_result;
     698        }
     699
     700        try {
    687701            // Reset original format.
    688702            $this->image->setImageFormat( $orig_format );
     
    704718            'mime-type' => $mime_type,
    705719        );
     720    }
     721
     722    /**
     723     * Writes an image to a file or stream.
     724     *
     725     * @since 5.6
     726     *
     727     * @param Imagick $image
     728     * @param string  $filename The destination filename or stream URL.
     729     *
     730     * @return true|WP_Error
     731     */
     732    private function write_image( $image, $filename ) {
     733        if ( wp_is_stream( $filename ) ) {
     734            /*
     735             * Due to reports of issues with streams with `Imagick::writeImageFile()` and `Imagick::writeImage()`, copies the blob instead.
     736             * Checks for exact type due to: https://www.php.net/manual/en/function.file-put-contents.php
     737             */
     738            if ( file_put_contents( $filename, $image->getImageBlob() ) === false ) {
     739                /* translators: %s: PHP function name. */
     740                return new WP_Error( 'image_save_error', sprintf( __( '%s failed while writing image to stream.' ), '<code>file_put_contents()</code>' ), $filename );
     741            } else {
     742                return true;
     743            }
     744        } else {
     745            try {
     746                return $image->writeImage( $filename );
     747            } catch ( Exception $e ) {
     748                return new WP_Error( 'image_save_error', $e->getMessage(), $filename );
     749            }
     750        }
    706751    }
    707752
  • trunk/src/wp-includes/class-wp-image-editor.php

    r48586 r49230  
    366366
    367367        if ( ! is_null( $dest_path ) ) {
    368             $_dest_path = realpath( $dest_path );
    369             if ( $_dest_path ) {
    370                 $dir = $_dest_path;
     368            if ( ! wp_is_stream( $dest_path ) ) {
     369                $_dest_path = realpath( $dest_path );
     370                if ( $_dest_path ) {
     371                    $dir = $_dest_path;
     372                }
     373            } else {
     374                $dir = $dest_path;
    371375            }
    372376        }
  • trunk/tests/phpunit/tests/image/editor.php

    r48937 r49230  
    144144        // Combo!
    145145        $this->assertSame( trailingslashit( realpath( get_temp_dir() ) ) . 'canola-new.png', $editor->generate_filename( 'new', realpath( get_temp_dir() ), 'png' ) );
     146
     147        // Test with a stream destination.
     148        $this->assertSame( 'file://testing/path/canola-100x50.jpg', $editor->generate_filename( null, 'file://testing/path' ) );
    146149    }
    147150
  • trunk/tests/phpunit/tests/image/editorImagick.php

    r49108 r49230  
    1717        require_once ABSPATH . WPINC . '/class-wp-image-editor.php';
    1818        require_once ABSPATH . WPINC . '/class-wp-image-editor-imagick.php';
     19        require_once DIR_TESTROOT . '/includes/class-wp-test-stream.php';
    1920
    2021        parent::setUp();
     
    575576    }
    576577
     578    /**
     579     * Test that images can be loaded and written over streams
     580     */
     581    public function test_streams() {
     582        stream_wrapper_register( 'wptest', 'WP_Test_Stream' );
     583        WP_Test_Stream::$data = array(
     584            'Tests_Image_Editor_Imagick' => array(
     585                '/read.jpg' => file_get_contents( DIR_TESTDATA . '/images/waffles.jpg' ),
     586            ),
     587        );
     588
     589        $file                 = 'wptest://Tests_Image_Editor_Imagick/read.jpg';
     590        $imagick_image_editor = new WP_Image_Editor_Imagick( $file );
     591
     592        $ret = $imagick_image_editor->load();
     593        $this->assertNotWPError( $ret );
     594
     595        $temp_file = 'wptest://Tests_Image_Editor_Imagick/write.jpg';
     596
     597        $ret = $imagick_image_editor->save( $temp_file );
     598        $this->assertNotWPError( $ret );
     599
     600        $this->assertSame( $temp_file, $ret['path'] );
     601
     602        if ( $temp_file !== $ret['path'] ) {
     603            unlink( $ret['path'] );
     604        }
     605        unlink( $temp_file );
     606    }
    577607}
Note: See TracChangeset for help on using the changeset viewer.