WordPress.org

Make WordPress Core

Changeset 36565


Ignore:
Timestamp:
02/17/16 22:51:01 (21 months 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.