WordPress.org

Make WordPress Core

Ticket #32075: 32075-improved-patch-v9.patch

File 32075-improved-patch-v9.patch, 17.0 KB (added by jrf, 5 years ago)

Rebased against current codebase

  • src/wp-admin/admin.php

    From 5542ed0101fd4f2f8c86e3c946109a27bd593480 Mon Sep 17 00:00:00 2001
    From: jrfnl <github_nospam@adviesenzo.nl>
    Date: Sun, 26 Jun 2016 12:54:05 +0200
    Subject: [PATCH] Prevent WP setting the memory limit to a value lower than it
     currently is.
    
    Also fixes a bug in how the memory limits were tested in the first place.
    Includes unit tests for the newly added functions in load.php.
    ---
     src/wp-admin/admin.php                            | 16 +---
     src/wp-admin/includes/file.php                    |  3 +-
     src/wp-admin/includes/image-edit.php              |  3 +-
     src/wp-includes/class-wp-image-editor-gd.php      | 12 +--
     src/wp-includes/class-wp-image-editor-imagick.php |  5 +-
     src/wp-includes/default-constants.php             | 43 +++++-----
     src/wp-includes/deprecated.php                    |  5 +-
     src/wp-includes/functions.php                     | 95 +++++++++++++++++++++++
     src/wp-includes/load.php                          | 58 ++++++++++++++
     tests/phpunit/tests/functions.php                 | 14 ++++
     tests/phpunit/tests/load.php                      | 72 +++++++++++++++++
     11 files changed, 268 insertions(+), 58 deletions(-)
     create mode 100644 tests/phpunit/tests/load.php
    
    diff --git a/src/wp-admin/admin.php b/src/wp-admin/admin.php
    index 6e9af5e..b35f073 100644
    a b else 
    138138        require(ABSPATH . 'wp-admin/menu.php');
    139139
    140140if ( current_user_can( 'manage_options' ) ) {
    141         /**
    142          * Filters the maximum memory limit available for administration screens.
    143          *
    144          * This only applies to administrators, who may require more memory for tasks like updates.
    145          * Memory limits when processing images (uploaded or edited by users of any role) are
    146          * handled separately.
    147          *
    148          * The WP_MAX_MEMORY_LIMIT constant specifically defines the maximum memory limit available
    149          * when in the administration back end. The default is 256M, or 256 megabytes of memory.
    150          *
    151          * @since 3.0.0
    152          *
    153          * @param string 'WP_MAX_MEMORY_LIMIT' The maximum WordPress memory limit. Default 256M.
    154          */
    155         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
     141        wp_raise_memory_limit( 'admin' );
    156142}
    157143
    158144/**
  • src/wp-admin/includes/file.php

    diff --git a/src/wp-admin/includes/file.php b/src/wp-admin/includes/file.php
    index 3b754df..f65b55c 100644
    a b function unzip_file($file, $to) { 
    570570                return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
    571571
    572572        // Unzip can use a lot of memory, but not this much hopefully
    573         /** This filter is documented in wp-admin/admin.php */
    574         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
     573        wp_raise_memory_limit( 'admin' );
    575574
    576575        $needed_dirs = array();
    577576        $to = trailingslashit($to);
  • src/wp-admin/includes/image-edit.php

    diff --git a/src/wp-admin/includes/image-edit.php b/src/wp-admin/includes/image-edit.php
    index 99e1c40..8b12a4e 100644
    a b function image_edit_apply_changes( $image, $changes ) { 
    586586function stream_preview_image( $post_id ) {
    587587        $post = get_post( $post_id );
    588588
    589         /** This filter is documented in wp-admin/admin.php */
    590         @ini_set( 'memory_limit', apply_filters( 'admin_memory_limit', WP_MAX_MEMORY_LIMIT ) );
     589        wp_raise_memory_limit( 'admin' );
    591590
    592591        $img = wp_get_image_editor( _load_image_to_edit_path( $post_id ) );
    593592
  • src/wp-includes/class-wp-image-editor-gd.php

    diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php
    index 96e7bf1..d7823eb 100644
    a b class WP_Image_Editor_GD extends WP_Image_Editor { 
    9696                if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) )
    9797                        return new WP_Error( 'error_loading_image', __('File doesn&#8217;t exist?'), $this->file );
    9898
    99                 /**
    100                  * Filters the memory limit allocated for image manipulation.
    101                  *
    102                  * @since 3.5.0
    103                  *
    104                  * @param int|string $limit Maximum memory limit to allocate for images. Default WP_MAX_MEMORY_LIMIT.
    105                  *                          Accepts an integer (bytes), or a shorthand string notation, such as '256M'.
    106                  */
    107                 $image_memory_limit = apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT );
    108 
    10999                // Set artificially high because GD uses uncompressed images in memory.
    110                 @ini_set( 'memory_limit', $image_memory_limit );
     100                wp_raise_memory_limit( 'image' );
    111101
    112102                $this->image = @imagecreatefromstring( file_get_contents( $this->file ) );
    113103
  • src/wp-includes/class-wp-image-editor-imagick.php

    diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php
    index 3d92d39..82b872d 100644
    a b class WP_Image_Editor_Imagick extends WP_Image_Editor { 
    137137                if ( ! is_file( $this->file ) && ! preg_match( '|^https?://|', $this->file ) )
    138138                        return new WP_Error( 'error_loading_image', __('File doesn&#8217;t exist?'), $this->file );
    139139
    140                 /** This filter is documented in wp-includes/class-wp-image-editor-gd.php */
    141                 $image_memory_limit = apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT );
    142 
    143140                /*
    144141                 * Even though Imagick uses less PHP memory than GD, set higher limit
    145142                 * for users that have low PHP.ini limits.
    146143                 */
    147                 @ini_set( 'memory_limit', $image_memory_limit );
     144                wp_raise_memory_limit( 'image' );
    148145
    149146                try {
    150147                        $this->image = new Imagick( $this->file );
  • src/wp-includes/default-constants.php

    diff --git a/src/wp-includes/default-constants.php b/src/wp-includes/default-constants.php
    index fe96e34..7a8ff57 100644
    a b  
    1717function wp_initial_constants() {
    1818        global $blog_id;
    1919
    20         // set memory limits
    21         if ( !defined('WP_MEMORY_LIMIT') ) {
    22                 if ( is_multisite() ) {
    23                         define('WP_MEMORY_LIMIT', '64M');
     20        $current_limit     = @ini_get( 'memory_limit' );
     21        $current_limit_int = wp_php_ini_bytes_to_int( $current_limit );
     22
     23        // Define memory limits.
     24        if ( ! defined( 'WP_MEMORY_LIMIT' ) ) {
     25                if ( false === wp_is_ini_value_changable( 'memory_limit' ) ) {
     26                        define( 'WP_MEMORY_LIMIT', $current_limit );
     27                } elseif ( is_multisite() ) {
     28                        define( 'WP_MEMORY_LIMIT', '64M' );
    2429                } else {
    25                         define('WP_MEMORY_LIMIT', '40M');
     30                        define( 'WP_MEMORY_LIMIT', '40M' );
    2631                }
    2732        }
    2833
    2934        if ( ! defined( 'WP_MAX_MEMORY_LIMIT' ) ) {
    30                 define( 'WP_MAX_MEMORY_LIMIT', '256M' );
     35                if ( false === wp_is_ini_value_changable( 'memory_limit' ) ) {
     36                        define( 'WP_MAX_MEMORY_LIMIT', $current_limit );
     37                } elseif ( -1 === $current_limit_int || $current_limit_int > 268435456 ) {
     38                        define( 'WP_MAX_MEMORY_LIMIT', $current_limit );
     39                } else {
     40                        define( 'WP_MAX_MEMORY_LIMIT', '256M' );
     41                }
    3142        }
    3243
     44        // Set memory limits.
     45        $wp_limit_int = wp_php_ini_bytes_to_int( WP_MEMORY_LIMIT );
     46        if ( -1 !== $current_limit_int && ( -1 === $wp_limit_int || $wp_limit_int > $current_limit_int ) ) {
     47                @ini_set( 'memory_limit', WP_MEMORY_LIMIT );
     48        }
     49
    3350        if ( ! isset($blog_id) )
    3451                $blog_id = 1;
    3552
    36         // set memory limits.
    37         if ( function_exists( 'memory_get_usage' ) ) {
    38                 $current_limit = @ini_get( 'memory_limit' );
    39                 $current_limit_int = intval( $current_limit );
    40                 if ( false !== strpos( $current_limit, 'G' ) )
    41                         $current_limit_int *= 1024;
    42                 $wp_limit_int = intval( WP_MEMORY_LIMIT );
    43                 if ( false !== strpos( WP_MEMORY_LIMIT, 'G' ) )
    44                         $wp_limit_int *= 1024;
    45 
    46                 if ( -1 != $current_limit && ( -1 == WP_MEMORY_LIMIT || $current_limit_int < $wp_limit_int ) )
    47                         @ini_set( 'memory_limit', WP_MEMORY_LIMIT );
    48         }
    49 
    5053        if ( !defined('WP_CONTENT_DIR') )
    5154                define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' ); // no trailing slash, full paths only - WP_CONTENT_URL is defined further down
    5255
  • src/wp-includes/deprecated.php

    diff --git a/src/wp-includes/deprecated.php b/src/wp-includes/deprecated.php
    index 0949f0b..8471a9b 100644
    a b function wp_load_image( $file ) { 
    31753175        if ( ! function_exists('imagecreatefromstring') )
    31763176                return __('The GD image library is not installed.');
    31773177
    3178         /** This filter is documented in wp-includes/class-wp-image-editor-gd.php */
    3179         $image_memory_limit = apply_filters( 'image_memory_limit', WP_MAX_MEMORY_LIMIT );
    3180 
    31813178        // Set artificially high because GD uses uncompressed images in memory.
    3182         @ini_set( 'memory_limit', $image_memory_limit );
     3179        wp_raise_memory_limit( 'image' );
    31833180
    31843181        $image = imagecreatefromstring( file_get_contents( $file ) );
    31853182
  • src/wp-includes/functions.php

    diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php
    index 690e271..16de2e6 100644
    a b function mysql_to_rfc3339( $date_string ) { 
    53505350        // Strip timezone information
    53515351        return preg_replace( '/(?:Z|[+-]\d{2}(?::\d{2})?)$/', '', $formatted );
    53525352}
     5353
     5354/**
     5355 * Attempts to raise the PHP memory limit for memory intensive processes.
     5356 *
     5357 * Allows only to raise the existing limit and prevents lowering it.
     5358 *
     5359 * @since 4.6.0
     5360 *
     5361 * @param string $context Context in which the function is called.
     5362 *                        Either 'admin' or 'image'. Defaults to 'admin'.
     5363 */
     5364function wp_raise_memory_limit( $context = 'admin' ) {
     5365        // Exit early if the limit cannot be changed.
     5366        if ( false === wp_is_ini_value_changable( 'memory_limit' ) ) {
     5367                return;
     5368        }
     5369
     5370        $current_limit     = @ini_get( 'memory_limit' );
     5371        $current_limit_int = wp_php_ini_bytes_to_int( $current_limit );
     5372
     5373        if ( -1 === $current_limit_int ) {
     5374                return;
     5375        }
     5376
     5377        $wp_max_limit     = WP_MAX_MEMORY_LIMIT;
     5378        $wp_max_limit_int = wp_php_ini_bytes_to_int( $wp_max_limit );
     5379        $filtered_limit   = $wp_max_limit;
     5380
     5381        switch ( $context ) {
     5382                case 'admin':
     5383                        /**
     5384                         * Filters the memory limit available for administration screens.
     5385                         *
     5386                         * This only applies to administrators, who may require more memory for tasks like updates.
     5387                         * Memory limits when processing images (uploaded or edited by users of any role) are
     5388                         * handled separately.
     5389                         *
     5390                         * The WP_MAX_MEMORY_LIMIT constant specifically defines the maximum memory limit available
     5391                         * when in the administration back end. The default is 256M (256 megabytes
     5392                         * of memory) or the original `memory_limit` php.ini value if this is higher.
     5393                         *
     5394                         * @since 3.0.0
     5395                         * @since 4.6.0 The default takes the original `memory_limit` into account.
     5396                         *
     5397                         * @param int|string $filtered_limit The maximum WordPress memory limit.
     5398                         *                                   Accepts an integer (bytes), or a shorthand string
     5399                         *                                   notation, such as '256M'.
     5400                         */
     5401                        $filtered_limit = apply_filters( 'admin_memory_limit', $filtered_limit );
     5402                        break;
     5403
     5404                case 'image':
     5405                        /**
     5406                         * Filters the memory limit allocated for image manipulation.
     5407                         *
     5408                         * @since 3.5.0
     5409                         * @since 4.6.0 The default takes the original `memory_limit` into account.
     5410                         *
     5411                         * @param int|string $filtered_limit Maximum memory limit to allocate for images.
     5412                         *                                   Default 256M or the original php.ini memory_limit,
     5413                         *                                   whichever is higher.
     5414                         *                                   Accepts an integer (bytes), or a shorthand string
     5415                         *                                   notation, such as '256M'.
     5416                         */
     5417                        $filtered_limit = apply_filters( 'image_memory_limit', $filtered_limit );
     5418                        break;
     5419
     5420                default:
     5421                        /**
     5422                         * Filters the memory limit allocated for arbitrary contexts.
     5423                         *
     5424                         * The dynamic portion of the hook name, `$context`, refers to an arbitrary
     5425                         * context passed on calling the function. This allows for plugins to define
     5426                         * their own contexts for raising the memory limit.
     5427                         *
     5428                         * @since 4.6.0
     5429                         *
     5430                         * @param int|string $filtered_limit Maximum memory limit to allocate for images.
     5431                         *                                   Default 256M or the original php.ini memory_limit,
     5432                         *                                   whichever is higher.
     5433                         *                                   Accepts an integer (bytes), or a shorthand string
     5434                         *                                   notation, such as '256M'.
     5435                         */
     5436                        $filtered_limit = apply_filters( "{$context}_memory_limit", $filtered_limit );
     5437                        break;
     5438        }
     5439
     5440        $filtered_limit_int = wp_php_ini_bytes_to_int( $filtered_limit );
     5441
     5442        if ( -1 === $filtered_limit_int || ( $filtered_limit_int > $wp_max_limit_int && $filtered_limit_int > $current_limit_int ) ) {
     5443                @ini_set( 'memory_limit', $filtered_limit );
     5444        } elseif ( -1 === $wp_max_limit_int || $wp_max_limit_int > $current_limit_int ) {
     5445                @ini_set( 'memory_limit', $wp_max_limit );
     5446        }
     5447}
  • src/wp-includes/load.php

    diff --git a/src/wp-includes/load.php b/src/wp-includes/load.php
    index 80654f1..66d3fca 100644
    a b function is_ssl() { 
    974974        }
    975975        return false;
    976976}
     977
     978/**
     979 * Converts a PHP ini shorthand byte value to an integer byte value.
     980 *
     981 * @since 4.6.0
     982 *
     983 * @see http://php.net/manual/en/function.ini-get.php
     984 * @see http://php.net/manual/en/faq.using.php#faq.using.shorthandbytes
     985 *
     986 * @param string $value An PHP ini byte value, either shorthand or ordinary.
     987 * @return int Value in bytes.
     988 */
     989function wp_php_ini_bytes_to_int( $value ) {
     990        $value = trim( $value );
     991        $last  = strtolower( $value[ strlen( $value ) - 1 ] );
     992
     993        switch( $last ) {
     994                // Note: the `break` statement is left out on purpose!
     995                case 'g':
     996                        $value *= 1024;
     997                case 'm':
     998                        $value *= 1024;
     999                case 'k':
     1000                        $value *= 1024;
     1001                default:
     1002                        // Left empty on purpose.
     1003                        break;
     1004        }
     1005
     1006        // Deal with large (float) values which run into the maximum integer size.
     1007        if ( PHP_INT_MAX < $value ) {
     1008                $value = PHP_INT_MAX;
     1009        }
     1010        return (int) $value;
     1011}
     1012
     1013/**
     1014 * Determines whether a PHP ini value is changable at runtime.
     1015 *
     1016 * @since 4.6.0
     1017 *
     1018 * @see http://php.net/manual/en/function.ini-get-all.php
     1019 *
     1020 * @param string $setting The name of the ini setting to check.
     1021 * @return bool True if the value is changable at runtime. False otherwise.
     1022 */
     1023function wp_is_ini_value_changable( $setting ) {
     1024        static $ini_all;
     1025
     1026        if ( ! isset( $ini_all ) ) {
     1027                $ini_all = ini_get_all();
     1028        }
     1029
     1030        if ( isset( $ini_all[ $setting ]['access'] ) && ( INI_ALL === $ini_all[ $setting ]['access'] || INI_USER === $ini_all[ $setting ]['access'] ) ) {
     1031                return true;
     1032        }
     1033        return false;
     1034}
  • tests/phpunit/tests/functions.php

    diff --git a/tests/phpunit/tests/functions.php b/tests/phpunit/tests/functions.php
    index e28e65f..6b5c8c9 100644
    a b class Tests_Functions extends WP_UnitTestCase { 
    887887
    888888                $this->assertNull( wp_ext2type( 'unknown_format' ) );
    889889        }
     890
     891        /**
     892         * Test raising the memory limit.
     893         *
     894         * {@internal Unfortunately as the default for 'WP_MAX_MEMORY_LIMIT' in the
     895         * test suite is -1, we can not test the memory limit negotiations.}}
     896         *
     897         * @ticket 32075
     898         */
     899        function test_wp_raise_memory_limit() {
     900                ini_set( 'memory_limit', '40M' );
     901                wp_raise_memory_limit();
     902                $this->assertEquals( '-1', ini_get( 'memory_limit' ) );
     903        }
    890904}
  • new file tests/phpunit/tests/load.php

    diff --git a/tests/phpunit/tests/load.php b/tests/phpunit/tests/load.php
    new file mode 100644
    index 0000000..84133ff
    - +  
     1<?php
     2
     3/**
     4 * @group load.php
     5 */
     6class Tests_Load extends WP_UnitTestCase {
     7
     8        /**
     9         * Test converting PHP ini byte values to integer byte values.
     10         *
     11         * @dataProvider data_wp_php_ini_bytes_to_int
     12         */
     13        function test_wp_php_ini_bytes_to_int( $value, $expected ) {
     14                $this->assertSame( $expected, wp_php_ini_bytes_to_int( $value ) );
     15        }
     16
     17        function data_wp_php_ini_bytes_to_int() {
     18                $array = array(
     19                        // Integer input
     20                        array( -1, -1 ), // = no memory limit
     21                        array( 8388608, 8388608 ), // 8M
     22
     23                        // String input (memory limit shorthand values)
     24                        array( '32k', 32768 ),
     25                        array( '64K', 65536 ),
     26                        array( '128m', 134217728 ),
     27                        array( '256M', 268435456 ),
     28                        array( '1g', 1073741824 ),
     29                        array( '1024', 1024 ), // No letter will be interpreted as integer value.
     30
     31                        // Edge cases
     32                        array( 'g', 0 ),
     33                        array( 'null', 0 ),
     34                        array( 'off', 0 ),
     35                );
     36
     37                // Test while running into maximum integer size limit on 32bit systems.
     38                if ( 2147483647 === PHP_INT_MAX ) {
     39                        $array[] = array( '2G', 2147483647 );
     40                        $array[] = array( '4G', 2147483647 );
     41                } else {
     42                        $array[] = array( '2G', 2147483648 );
     43                        $array[] = array( '4G', 4294967296 );
     44                }
     45
     46                return $array;
     47        }
     48
     49        /**
     50         * Test the determining of the changability of a PHP ini value.
     51         *
     52         * @dataProvider data_wp_is_ini_value_changable
     53         */
     54        function test_wp_is_ini_value_changable( $setting, $expected ) {
     55                $this->assertSame( $expected, wp_is_ini_value_changable( $setting ) );
     56        }
     57
     58        function data_wp_is_ini_value_changable() {
     59                $array = array(
     60                        array( 'memory_limit', true ), // PHP_INI_ALL
     61                        array( 'log_errors', true ), // PHP_INI_ALL
     62                        array( 'upload_max_filesize', false ), // PHP_INI_PERDIR
     63                        array( 'upload_tmp_dir', false ), // PHP_INI_SYSTEM
     64                );
     65
     66                if ( extension_loaded( 'Tidy' ) && version_compare( PHP_VERSION, '7.0.0', '>' ) ) {
     67                        $array[] = array( 'tidy.clean_output', true ); // PHP_INI_USER
     68                }
     69
     70                return $array;
     71        }
     72}