Make WordPress Core

Ticket #6531: 6531.11.diff

File 6531.11.diff, 11.0 KB (added by schlessera, 7 years ago)

Recursive approach + tests

  • src/wp-admin/includes/file.php

    diff --git src/wp-admin/includes/file.php src/wp-admin/includes/file.php
    index 553880a46c..c93dc9e0d7 100644
    function get_home_path() { 
    123123 *
    124124 * @param string $folder Optional. Full path to folder. Default empty.
    125125 * @param int    $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit).
     126 * @param array  $exclusions Optional. List of folders to skip.
    126127 * @return bool|array False on failure, Else array of files
    127128 */
    128 function list_files( $folder = '', $levels = 100 ) {
    129         if ( empty($folder) )
     129function list_files( $folder = '', $levels = 100, $exclusions = array() ) {
     130        if ( empty( $folder ) ) {
    130131                return false;
     132        }
     133
     134        $folder = trailingslashit( $folder );
    131135
    132         if ( ! $levels )
     136        if ( ! $levels ) {
    133137                return false;
     138        }
    134139
    135140        $files = array();
    136         if ( $dir = @opendir( $folder ) ) {
    137                 while (($file = readdir( $dir ) ) !== false ) {
    138                         if ( in_array($file, array('.', '..') ) )
     141
     142        $dir = @opendir( $folder );
     143        if ( $dir ) {
     144                while ( ( $file = readdir( $dir ) ) !== false ) {
     145                        // Skip current and parent folder links.
     146                        if ( in_array( $file, array( '.', '..' ), true ) ) {
     147                                continue;
     148                        }
     149
     150                        // Skip hidden and excluded files.
     151                        if ( '.' === $file[0] || in_array( $file, $exclusions, true ) ) {
    139152                                continue;
    140                         if ( is_dir( $folder . '/' . $file ) ) {
    141                                 $files2 = list_files( $folder . '/' . $file, $levels - 1);
    142                                 if ( $files2 )
     153                        }
     154
     155                        if ( is_dir( $folder . $file ) ) {
     156                                $files2 = list_files( $folder . $file, $levels - 1 );
     157                                if ( $files2 ) {
    143158                                        $files = array_merge($files, $files2 );
    144                                 else
    145                                         $files[] = $folder . '/' . $file . '/';
     159                                } else {
     160                                        $files[] = $folder . $file . '/';
     161                                }
    146162                        } else {
    147                                 $files[] = $folder . '/' . $file;
     163                                $files[] = $folder . $file;
    148164                        }
    149165                }
    150166        }
    151167        @closedir( $dir );
     168
    152169        return $files;
    153170}
    154171
  • src/wp-admin/includes/plugin.php

    diff --git src/wp-admin/includes/plugin.php src/wp-admin/includes/plugin.php
    index 69aeacda2f..272b333a68 100644
    function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup 
    190190 * @param string $plugin Path to the main plugin file from plugins directory.
    191191 * @return array List of files relative to the plugin root.
    192192 */
    193 function get_plugin_files($plugin) {
     193function get_plugin_files( $plugin ) {
    194194        $plugin_file = WP_PLUGIN_DIR . '/' . $plugin;
    195         $dir = dirname($plugin_file);
    196         $plugin_files = array($plugin);
    197         if ( is_dir($dir) && $dir != WP_PLUGIN_DIR ) {
    198                 $plugins_dir = @ opendir( $dir );
    199                 if ( $plugins_dir ) {
    200                         while (($file = readdir( $plugins_dir ) ) !== false ) {
    201                                 if ( substr($file, 0, 1) == '.' )
    202                                         continue;
    203                                 if ( is_dir( $dir . '/' . $file ) ) {
    204                                         $plugins_subdir = @ opendir( $dir . '/' . $file );
    205                                         if ( $plugins_subdir ) {
    206                                                 while (($subfile = readdir( $plugins_subdir ) ) !== false ) {
    207                                                         if ( substr($subfile, 0, 1) == '.' )
    208                                                                 continue;
    209                                                         $plugin_files[] = plugin_basename("$dir/$file/$subfile");
    210                                                 }
    211                                                 @closedir( $plugins_subdir );
    212                                         }
    213                                 } else {
    214                                         if ( plugin_basename("$dir/$file") != $plugin )
    215                                                 $plugin_files[] = plugin_basename("$dir/$file");
    216                                 }
    217                         }
    218                         @closedir( $plugins_dir );
    219                 }
     195        $dir = dirname( $plugin_file );
     196
     197        $data = get_plugin_data( $plugin_file );
     198        $label = isset( $data['Version'] )
     199                ? sanitize_key( 'files_' . $plugin . '-' . $data['Version'] )
     200                : sanitize_key( 'files_' . $plugin );
     201        $transient_key = substr( $label, 0, 29 ) . md5( $label );
     202
     203        $plugin_files = false; //get_transient( $transient_key );
     204        if ( false !== $plugin_files ) {
     205                return $plugin_files;
    220206        }
    221207
     208        $plugin_files = array( plugin_basename( $plugin_file ) );
     209
     210        if ( is_dir( $dir ) && WP_PLUGIN_DIR !== $dir ) {
     211
     212                /**
     213                 * Filters the array of excluded directories and files while scanning the folder.
     214                 *
     215                 * @since 4.9
     216                 *
     217                 * @param array $exclusions Array of excluded directories and files.
     218                 */
     219                $exclusions = (array) apply_filters( 'get_plugin_files_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
     220
     221                $list_files = list_files( $dir, 100, $exclusions );
     222                $list_files = array_map( 'plugin_basename', $list_files );
     223
     224                $plugin_files += $list_files;
     225                $plugin_files = array_unique( $plugin_files );
     226        }
     227
     228        set_transient( $transient_key, $plugin_files, HOUR_IN_SECONDS );
     229
    222230        return $plugin_files;
    223231}
    224232
  • src/wp-admin/theme-editor.php

    diff --git src/wp-admin/theme-editor.php src/wp-admin/theme-editor.php
    index ebac61b538..70629d914d 100644
    $file_types = wp_get_theme_file_editable_extensions( $theme ); 
    7575foreach ( $file_types as $type ) {
    7676        switch ( $type ) {
    7777                case 'php':
    78                         $allowed_files += $theme->get_files( 'php', 1 );
     78                        $allowed_files += $theme->get_files( 'php', -1 );
    7979                        $has_templates = ! empty( $allowed_files );
    8080                        break;
    8181                case 'css':
    82                         $style_files = $theme->get_files( 'css' );
     82                        $style_files = $theme->get_files( 'css', -1 );
    8383                        $allowed_files['style.css'] = $style_files['style.css'];
    8484                        $allowed_files += $style_files;
    8585                        break;
    8686                default:
    87                         $allowed_files += $theme->get_files( $type );
     87                        $allowed_files += $theme->get_files( $type, -1 );
    8888                        break;
    8989        }
    9090}
  • src/wp-includes/class-wp-theme.php

    diff --git src/wp-includes/class-wp-theme.php src/wp-includes/class-wp-theme.php
    index 30e9226132..4dd0a75016 100644
    final class WP_Theme implements ArrayAccess { 
    981981         * @param int $depth Optional. How deep to search for files. Defaults to a flat scan (0 depth). -1 depth is infinite.
    982982         * @param bool $search_parent Optional. Whether to return parent files. Defaults to false.
    983983         * @return array Array of files, keyed by the path to the file relative to the theme's directory, with the values
    984          *                   being absolute paths.
     984         *               being absolute paths.
    985985         */
    986986        public function get_files( $type = null, $depth = 0, $search_parent = false ) {
    987                 $files = (array) self::scandir( $this->get_stylesheet_directory(), $type, $depth );
     987                // get and cache all theme files to start with.
     988                $label = sanitize_key( 'files_' . $this->cache_hash . '-' . $this->get( 'Version' ) );
     989                $transient_key = substr( $label, 0, 29 ) . md5( $label );
    988990
    989                 if ( $search_parent && $this->parent() )
    990                         $files += (array) self::scandir( $this->get_template_directory(), $type, $depth );
     991                $all_files = get_transient( $transient_key );
     992                if ( false === $all_files ) {
     993                        $all_files = (array) self::scandir( $this->get_stylesheet_directory(), null, -1 );
     994
     995                        if ( $search_parent && $this->parent() ) {
     996                                $all_files += (array) self::scandir( $this->get_template_directory(), null, -1 );
     997                        }
     998
     999                        set_transient( $transient_key, $all_files, HOUR_IN_SECONDS );
     1000                }
     1001
     1002                // Filter $all_files by $type & $depth.
     1003                $files = array();
     1004                if ( $type ) {
     1005                        $type = (array) $type;
     1006                        $_extensions = implode( '|', $type );
     1007                }
     1008                foreach ( $all_files as $key => $file ) {
     1009                        if ( $depth >= 0 && substr_count( $key, '/' ) > $depth ) {
     1010                                continue; // Filter by depth.
     1011                        }
     1012                        if ( ! $type || preg_match( '~\.(' . $_extensions . ')$~', $file ) ) { // Filter by type.
     1013                                $files[ $key ] = $file;
     1014                        }
     1015                }
    9911016
    9921017                return $files;
    9931018        }
    final class WP_Theme implements ArrayAccess { 
    11071132         *                     with `$relative_path`, with the values being absolute paths. False otherwise.
    11081133         */
    11091134        private static function scandir( $path, $extensions = null, $depth = 0, $relative_path = '' ) {
    1110                 if ( ! is_dir( $path ) )
     1135                if ( ! is_dir( $path ) ) {
    11111136                        return false;
     1137                }
    11121138
    11131139                if ( $extensions ) {
    11141140                        $extensions = (array) $extensions;
    final class WP_Theme implements ArrayAccess { 
    11161142                }
    11171143
    11181144                $relative_path = trailingslashit( $relative_path );
    1119                 if ( '/' == $relative_path )
     1145                if ( '/' == $relative_path ) {
    11201146                        $relative_path = '';
     1147                }
    11211148
    11221149                $results = scandir( $path );
    11231150                $files = array();
    final class WP_Theme implements ArrayAccess { 
    11251152                /**
    11261153                 * Filters the array of excluded directories and files while scanning theme folder.
    11271154                 *
    1128                  * @since 4.7.4
     1155                 * @since 4.7.4
    11291156                 *
    11301157                 * @param array $exclusions Array of excluded directories and files.
    11311158                 */
    1132                 $exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules' ) );
     1159                $exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
    11331160
    11341161                foreach ( $results as $result ) {
    11351162                        if ( '.' == $result[0] || in_array( $result, $exclusions, true ) ) {
    11361163                                continue;
    11371164                        }
    11381165                        if ( is_dir( $path . '/' . $result ) ) {
    1139                                 if ( ! $depth )
     1166                                if ( ! $depth ) {
    11401167                                        continue;
     1168                                }
    11411169                                $found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result );
    11421170                                $files = array_merge_recursive( $files, $found );
    11431171                        } elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) {
  • tests/phpunit/tests/admin/includesPlugin.php

    diff --git tests/phpunit/tests/admin/includesPlugin.php tests/phpunit/tests/admin/includesPlugin.php
    index b7ed20dd90..29bc84dfe5 100644
    class Tests_Admin_includesPlugin extends WP_UnitTestCase { 
    9393                $this->assertEquals( array( $name ), get_plugin_files( $name ) );
    9494        }
    9595
     96        /**
     97         * @covers ::get_plugin_files
     98         */
     99        public function test_get_plugin_files_folder() {
     100                $plugin_dir = WP_PLUGIN_DIR . '/list_files_test_plugin';
     101                @mkdir( $plugin_dir );
     102                $plugin = $this->_create_plugin(null, 'list_files_test_plugin.php', $plugin_dir );
     103
     104                $sub_dir = trailingslashit( dirname( $plugin[1] ) ) . 'subdir';
     105                @mkdir( $sub_dir );
     106                @file_put_contents( $sub_dir . '/subfile.php', '<?php // Silence.' );
     107
     108                $plugin_files = get_plugin_files( plugin_basename( $plugin[1] ) );
     109                $expected = array(
     110                        'list_files_test_plugin/list_files_test_plugin.php',
     111                        'list_files_test_plugin/subdir/subfile.php',
     112                );
     113                $this->assertEquals( $expected, $plugin_files );
     114
     115                unlink( $sub_dir . '/subfile.php' );
     116                unlink( $plugin[1] );
     117                rmdir( $sub_dir );
     118                rmdir( $plugin_dir );
     119        }
     120
    96121        /**
    97122         * @covers ::get_mu_plugins
    98123         */
  • new file tests/phpunit/tests/functions/listFiles.php

    diff --git tests/phpunit/tests/functions/listFiles.php tests/phpunit/tests/functions/listFiles.php
    new file mode 100644
    index 0000000000..c464382fe2
    - +  
     1<?php
     2
     3/**
     4 * Test list_files().
     5 *
     6 * @group functions.php
     7 */
     8class Tests_Functions_ListFiles extends WP_UnitTestCase {
     9
     10        public function test_list_files_returns_a_list_of_files() {
     11                $admin_files = list_files( ABSPATH . 'wp-admin/' );
     12                $this->assertInternalType( 'array', $admin_files );
     13                $this->assertNotEmpty( $admin_files );
     14                $this->assertTrue( in_array( ABSPATH . 'wp-admin/about.php', $admin_files, true ) );
     15        }
     16
     17        public function test_list_files_can_exclude_files() {
     18                $admin_files = list_files( ABSPATH . 'wp-admin/', 100, array( 'about.php' ) );
     19                $this->assertFalse( in_array( ABSPATH . 'wp-admin/about.php', $admin_files, true ) );
     20        }
     21}