Make WordPress Core

Changeset 36565


Ignore:
Timestamp:
02/17/2016 10:51:01 PM (9 years ago)
Author:
azaozz
Message:

Improve the performance of wp_upload_dir():

  • Cache the output in non-persistent cache.
  • Cache the result from wp_mkdir_p() in persistent cache (when present).
  • Introduce wp_get_upload_dir() for use when not uploading files. It is equivalent to wp_upload_dir() but does not check for the existence or create the upload directory.
  • Change tests to use the non-cached _wp_upload_dir(). They change options on the fly (should never be used in production) to simulate different environments.
  • Introduce _upload_dir_no_subdir() and _upload_dir_https() to facilitate testing. These use the proper upload_dir filter to simulate different environments.

Props kovshenin, azaozz.
See #34359.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/functions.php

    r36541 r36565  
    18011801
    18021802/**
     1803 * Get uploads directory information.
     1804 *
     1805 * Same as wp_upload_dir() but "light weight" as it doesn't attempt to create the uploads directory.
     1806 * Intended for use in themes, when only 'basedir' and 'baseurl' are needed, generally in all cases when not uploading files.
     1807 *
     1808 * @since 4.5.0
     1809 *
     1810 * @return array See wp_upload_dir() for description.
     1811 */
     1812function wp_get_upload_dir() {
     1813    return wp_upload_dir( null, false );
     1814}
     1815
     1816/**
    18031817 * Get an array containing the current upload directory's path and url.
    18041818 *
     
    18251839 * 'basedir' - path without subdir.
    18261840 * 'baseurl' - URL path without subdir.
    1827  * 'error' - set to false.
     1841 * 'error' - false or error message.
    18281842 *
    18291843 * @since 2.0.0
     1844 * @uses _wp_upload_dir()
    18301845 *
    18311846 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null.
     1847 * @param bool   $create_dir Optional. Whether to check and create the uploads directory. Default true (backwards compatible).
     1848 * @param bool   $refresh_cache Optional. Whether to refresh the cache. Default false.
    18321849 * @return array See above for description.
    18331850 */
    1834 function wp_upload_dir( $time = null ) {
     1851function wp_upload_dir( $time = null, $create_dir = true, $refresh_cache = false ) {
     1852    static $cache = array();
     1853
     1854    $key = sprintf( '%d-%s', get_current_blog_id(), (string) $time );
     1855
     1856    if ( $refresh_cache || empty( $cache[ $key ] ) ) {
     1857        $cache[ $key ] = _wp_upload_dir( $time );
     1858    }
     1859
     1860    /**
     1861     * Filter the uploads directory data.
     1862     *
     1863     * @since 2.0.0
     1864     *
     1865     * @param array $uploads Array of upload directory data with keys of 'path',
     1866     *                       'url', 'subdir, 'basedir', and 'error'.
     1867     */
     1868    $uploads = apply_filters( 'upload_dir', $cache[ $key ] );
     1869
     1870    if ( $create_dir ) {
     1871        $path = $uploads['path'];
     1872        $tested_paths = wp_cache_get( 'upload_dir_tested_paths' );
     1873
     1874        if ( ! is_array( $tested_paths ) ) {
     1875            $tested_paths = array();
     1876        }
     1877
     1878        if ( array_key_exists( $path, $tested_paths ) ) {
     1879            $uploads['error'] = $tested_paths[ $path ];
     1880        } else {
     1881            if ( ! wp_mkdir_p( $path ) ) {
     1882                if ( 0 === strpos( $uploads['basedir'], ABSPATH ) ) {
     1883                    $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
     1884                } else {
     1885                    $error_path = basename( $uploads['basedir'] ) . $uploads['subdir'];
     1886                }
     1887
     1888                $uploads['error'] = sprintf( __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), $error_path );
     1889            }
     1890
     1891            $tested_paths[ $path ] = $uploads['error'];
     1892            wp_cache_set( 'upload_dir_tested_paths', $tested_paths );
     1893        }
     1894    }
     1895
     1896    return $uploads;
     1897}
     1898
     1899/**
     1900 * A non-filtered, non-cached version of wp_upload_dir() that doesn't check the path.
     1901 *
     1902 * @access private
     1903 *
     1904 * @param string $time Optional. Time formatted in 'yyyy/mm'. Default null.
     1905 * @return array See wp_upload_dir()
     1906 */
     1907function _wp_upload_dir( $time = null ) {
    18351908    $siteurl = get_option( 'siteurl' );
    18361909    $upload_path = trim( get_option( 'upload_path' ) );
     
    19211994    $url .= $subdir;
    19221995
    1923     /**
    1924      * Filter the uploads directory data.
    1925      *
    1926      * @since 2.0.0
    1927      *
    1928      * @param array $uploads Array of upload directory data with keys of 'path',
    1929      *                       'url', 'subdir, 'basedir', and 'error'.
    1930      */
    1931     $uploads = apply_filters( 'upload_dir',
    1932         array(
    1933             'path'    => $dir,
    1934             'url'     => $url,
    1935             'subdir'  => $subdir,
    1936             'basedir' => $basedir,
    1937             'baseurl' => $baseurl,
    1938             'error'   => false,
    1939         ) );
    1940 
    1941     // Make sure we have an uploads directory.
    1942     if ( ! wp_mkdir_p( $uploads['path'] ) ) {
    1943         if ( 0 === strpos( $uploads['basedir'], ABSPATH ) )
    1944             $error_path = str_replace( ABSPATH, '', $uploads['basedir'] ) . $uploads['subdir'];
    1945         else
    1946             $error_path = basename( $uploads['basedir'] ) . $uploads['subdir'];
    1947 
    1948         $message = sprintf( __( 'Unable to create directory %s. Is its parent directory writable by the server?' ), $error_path );
    1949         $uploads['error'] = $message;
    1950     }
    1951 
    1952     return $uploads;
     1996    return array(
     1997        'path'    => $dir,
     1998        'url'     => $url,
     1999        'subdir'  => $subdir,
     2000        'basedir' => $basedir,
     2001        'baseurl' => $baseurl,
     2002        'error'   => false,
     2003    );
    19532004}
    19542005
  • trunk/tests/phpunit/includes/functions.php

    r32139 r36565  
    116116    update_option( 'permalink_structure', '/%year%/%monthnum%/%day%/%postname%/' );
    117117}
     118
     119/**
     120 * Helper used with the `upload_dir` filter to remove the /year/month sub directories from the uploads path and URL.
     121 */
     122function _upload_dir_no_subdir( $uploads ) {
     123    $subdir = $uploads['subdir'];
     124
     125    $uploads['subdir'] = '';
     126    $uploads['path'] = str_replace( $subdir, '', $uploads['path'] );
     127    $uploads['url'] = str_replace( $subdir, '', $uploads['url'] );
     128
     129    return $uploads;
     130}
     131
     132/**
     133 * Helper used with the `upload_dir` filter to set https upload URL.
     134 */
     135function _upload_dir_https( $uploads ) {
     136    $uploads['url'] = str_replace( 'http://', 'https://', $uploads['url'] );
     137    $uploads['baseurl'] = str_replace( 'http://', 'https://', $uploads['baseurl'] );
     138
     139    return $uploads;
     140}
  • trunk/tests/phpunit/tests/media.php

    r36240 r36565  
    896896        global $_wp_additional_image_sizes;
    897897
    898         // Save the current setting for uploads folders
    899         $uploads_use_yearmonth_folders = get_option( 'uploads_use_yearmonth_folders' );
    900 
    901898        // Disable date organized uploads
    902         update_option( 'uploads_use_yearmonth_folders', 0 );
     899        add_filter( 'upload_dir', '_upload_dir_no_subdir' );
    903900
    904901        // Make an image.
     
    938935        // Remove the attachment
    939936        wp_delete_attachment( $id );
    940 
    941         // Leave the uploads option the way you found it.
    942         update_option( 'uploads_use_yearmonth_folders', $uploads_use_yearmonth_folders );
     937        remove_filter( 'upload_dir', '_upload_dir_no_subdir' );
    943938    }
    944939
  • trunk/tests/phpunit/tests/post/attachments.php

    r35479 r36565  
    442442     */
    443443    public function test_wp_get_attachment_url_should_force_https_when_administering_over_https_and_siteurl_is_https() {
    444         // Must set the upload_url_path to fake out `wp_upload_dir()`.
    445         $siteurl = get_option( 'siteurl' );
    446         update_option( 'upload_url_path', set_url_scheme( $siteurl, 'https' ) . '/uploads' );
     444        // Set https upload URL
     445        add_filter( 'upload_dir', '_upload_dir_https' );
    447446
    448447        $filename = ( DIR_TESTDATA . '/images/test-image.jpg' );
     
    464463        $_SERVER['HTTPS'] = $is_ssl ? 'on' : 'off';
    465464        set_current_screen( 'front' );
     465        remove_filter( 'upload_dir', '_upload_dir_https' );
    466466
    467467        $this->assertSame( set_url_scheme( $url, 'https' ), $url );
  • trunk/tests/phpunit/tests/upload.php

    r30658 r36565  
    2727        // wp_upload_dir() with default parameters
    2828        $info = wp_upload_dir();
    29         $this->assertEquals( get_option( 'siteurl' ) . '/wp-content/uploads/' . gmstrftime('%Y/%m'), $info['url'] );
    30         $this->assertEquals( ABSPATH . 'wp-content/uploads/' . gmstrftime('%Y/%m'), $info['path'] );
    31         $this->assertEquals( gmstrftime('/%Y/%m'), $info['subdir'] );
    32         $this->assertEquals( '', $info['error'] );
     29        $subdir = gmstrftime('/%Y/%m');
     30
     31        $this->assertEquals( get_option( 'siteurl' ) . '/wp-content/uploads' . $subdir, $info['url'] );
     32        $this->assertEquals( ABSPATH . 'wp-content/uploads' . $subdir, $info['path'] );
     33        $this->assertEquals( $subdir, $info['subdir'] );
     34        $this->assertEquals( false, $info['error'] );
    3335    }
    3436
     
    3638        // wp_upload_dir() with a relative upload path that is not 'wp-content/uploads'
    3739        update_option( 'upload_path', 'foo/bar' );
    38         $info = wp_upload_dir();
    39         $this->delete_folders( ABSPATH . 'foo' );
     40        $info = _wp_upload_dir();
     41        $subdir = gmstrftime('/%Y/%m');
    4042
    41         $this->assertEquals( get_option( 'siteurl' ) . '/foo/bar/' . gmstrftime('%Y/%m'), $info['url'] );
    42         $this->assertEquals( ABSPATH . 'foo/bar/' . gmstrftime('%Y/%m'), $info['path'] );
    43         $this->assertEquals( gmstrftime('/%Y/%m'), $info['subdir'] );
    44         $this->assertEquals( '', $info['error'] );
     43        $this->assertEquals( get_option( 'siteurl' ) . '/foo/bar' . $subdir, $info['url'] );
     44        $this->assertEquals( ABSPATH . 'foo/bar' . $subdir, $info['path'] );
     45        $this->assertEquals( $subdir, $info['subdir'] );
     46        $this->assertEquals( false, $info['error'] );
    4547    }
    4648
     
    5052    function test_upload_dir_absolute() {
    5153        $path = '/tmp/wp-unit-test';
     54
    5255        // wp_upload_dir() with an absolute upload path
    5356        update_option( 'upload_path', $path );
     57
    5458        // doesn't make sense to use an absolute file path without setting the url path
    5559        update_option( 'upload_url_path', '/baz' );
    56         $info = wp_upload_dir();
    57         $this->delete_folders( $path );
    5860
    59         $this->assertEquals( '/baz/' . gmstrftime('%Y/%m'), $info['url'] );
    60         $this->assertEquals( "$path/" . gmstrftime('%Y/%m'), $info['path'] );
    61         $this->assertEquals( gmstrftime('/%Y/%m'), $info['subdir'] );
    62         $this->assertEquals( '', $info['error'] );
     61        // Use `_wp_upload_dir()` directly to bypass caching and work with the changed options.
     62        // It doesn't create the /year/month directories.
     63        $info = _wp_upload_dir();
     64        $subdir = gmstrftime('/%Y/%m');
     65
     66        $this->assertEquals( '/baz' . $subdir, $info['url'] );
     67        $this->assertEquals( $path . $subdir, $info['path'] );
     68        $this->assertEquals( $subdir, $info['subdir'] );
     69        $this->assertEquals( false, $info['error'] );
    6370    }
    6471
    6572    function test_upload_dir_no_yearnum() {
    6673        update_option( 'uploads_use_yearmonth_folders', 0 );
    67         $info = wp_upload_dir();
     74
     75        // Use `_wp_upload_dir()` directly to bypass caching and work with the changed options.
     76        $info = _wp_upload_dir();
     77
    6878        $this->assertEquals( get_option( 'siteurl' ) . '/wp-content/uploads', $info['url'] );
    6979        $this->assertEquals( ABSPATH . 'wp-content/uploads', $info['path'] );
    7080        $this->assertEquals( '', $info['subdir'] );
    71         $this->assertEquals( '', $info['error'] );
     81        $this->assertEquals( false, $info['error'] );
    7282    }
    7383
    7484    function test_upload_path_absolute() {
    7585        update_option( 'upload_url_path', 'http://example.org/asdf' );
    76         $info = wp_upload_dir();
    77         $this->assertEquals( 'http://example.org/asdf/' . gmstrftime('%Y/%m'), $info['url'] );
    78         $this->assertEquals( ABSPATH . 'wp-content/uploads/' . gmstrftime('%Y/%m'), $info['path'] );
    79         $this->assertEquals( gmstrftime('/%Y/%m'), $info['subdir'] );
    80         $this->assertEquals( '', $info['error'] );
     86
     87        // Use `_wp_upload_dir()` directly to bypass caching and work with the changed options.
     88        // It doesn't create the /year/month directories.
     89        $info = _wp_upload_dir();
     90        $subdir = gmstrftime('/%Y/%m');
     91
     92        $this->assertEquals( 'http://example.org/asdf' . $subdir, $info['url'] );
     93        $this->assertEquals( ABSPATH . 'wp-content/uploads' . $subdir, $info['path'] );
     94        $this->assertEquals( $subdir, $info['subdir'] );
     95        $this->assertEquals( false, $info['error'] );
    8196    }
    8297
     
    8499        // upload path setting is empty - it should default to 'wp-content/uploads'
    85100        update_option('upload_path', '');
    86         $info = wp_upload_dir();
    87         $this->assertEquals( get_option( 'siteurl' ) . '/wp-content/uploads/' . gmstrftime('%Y/%m'), $info['url'] );
    88         $this->assertEquals( ABSPATH . 'wp-content/uploads/' . gmstrftime('%Y/%m'), $info['path'] );
    89         $this->assertEquals( gmstrftime('/%Y/%m'), $info['subdir'] );
    90         $this->assertEquals( '', $info['error'] );
     101
     102        // Use `_wp_upload_dir()` directly to bypass caching and work with the changed options.
     103        // It doesn't create the /year/month directories.
     104        $info = _wp_upload_dir();
     105        $subdir = gmstrftime('/%Y/%m');
     106
     107        $this->assertEquals( get_option( 'siteurl' ) . '/wp-content/uploads' . $subdir, $info['url'] );
     108        $this->assertEquals( ABSPATH . 'wp-content/uploads' . $subdir, $info['path'] );
     109        $this->assertEquals( $subdir, $info['subdir'] );
     110        $this->assertEquals( false, $info['error'] );
    91111    }
    92112
Note: See TracChangeset for help on using the changeset viewer.