Make WordPress Core


Ignore:
Timestamp:
02/22/2023 02:23:50 PM (3 years ago)
Author:
SergeyBiryukov
Message:

Media: Add WP_Image_Editor_Imagick::set_imagick_time_limit() method.

This aims to avoid timeout in Imagick operations.

Previously, Imagick operations could silently error by timeout and produce unexpected results. The new ::set_imagick_time_limit() method, now used in ::resize() and ::crop(), will better handle garbage collection in these cases as well as better align Imagick's timeout with PHP timeout, assuming it is set.

Props drzraf, audrasjb, costdev, antpb, SergeyBiryukov.
Fixes #52569.

File:
1 edited

Legend:

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

    r55300 r55404  
    255255
    256256        /**
     257         * Sets Imagick time limit.
     258         *
     259         * Depending on configuration, Imagick processing may take time.
     260         *
     261         * Multiple problems exist if PHP times out before ImageMagick completed:
     262         * 1. Temporary files aren't cleaned by ImageMagick garbage collection.
     263         * 2. No clear error is provided.
     264         * 3. The cause of such timeout can be hard to pinpoint.
     265         *
     266         * This function, which is expected to be run before heavy image routines, resolves
     267         * point 1 above by aligning Imagick's timeout with PHP's timeout, assuming it is set.
     268         *
     269         * Note:
     270         *  - Imagick resource exhaustion does not issue catchable exceptions (yet).
     271         *    See https://github.com/Imagick/imagick/issues/333.
     272         *  - The resource limit is not saved/restored. It applies to subsequent
     273         *    image operations within the time of the HTTP request.
     274         *
     275         * @since 6.2.0
     276         *
     277         * @return int|null The new limit on success, null on failure.
     278         */
     279        public static function set_imagick_time_limit() {
     280                if ( ! defined( 'Imagick::RESOURCETYPE_TIME' ) ) {
     281                        return null;
     282                }
     283
     284                // Returns PHP_FLOAT_MAX if unset.
     285                $imagick_timeout = Imagick::getResourceLimit( Imagick::RESOURCETYPE_TIME );
     286
     287                // Convert to an integer, keeping in mind that: 0 === (int) PHP_FLOAT_MAX.
     288                $imagick_timeout = $imagick_timeout > PHP_INT_MAX ? PHP_INT_MAX : (int) $imagick_timeout;
     289
     290                $php_timeout = (int) ini_get( 'max_execution_time' );
     291
     292                if ( $php_timeout > 1 && $php_timeout < $imagick_timeout ) {
     293                        $limit = (float) 0.8 * $php_timeout;
     294                        Imagick::setResourceLimit( Imagick::RESOURCETYPE_TIME, $limit );
     295
     296                        return $limit;
     297                }
     298        }
     299
     300        /**
    257301         * Resizes current image.
    258302         *
     
    283327                        return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h );
    284328                }
     329
     330                self::set_imagick_time_limit();
    285331
    286332                // Execute the resize.
     
    550596                }
    551597
     598                self::set_imagick_time_limit();
     599
    552600                try {
    553601                        $this->image->cropImage( $src_w, $src_h, $src_x, $src_y );
Note: See TracChangeset for help on using the changeset viewer.