Make WordPress Core


Ignore:
Timestamp:
02/22/2023 02:23:50 PM (19 months 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.