Ticket #6531: 6531.10.diff
File 6531.10.diff, 13.8 KB (added by , 7 years ago) |
---|
-
src/wp-admin/includes/file.php
diff --git src/wp-admin/includes/file.php src/wp-admin/includes/file.php index 553880a46c..0135771f6c 100644
function get_home_path() { 116 116 } 117 117 118 118 /** 119 * Returns a listing of all files in the specified folder and all subdirectories up to 100 levels deep.119 * Returns a listing of all files in the specified folder and all subdirectories. 120 120 * The depth of the recursiveness can be controlled by the $levels param. 121 121 * 122 122 * @since 2.6.0 123 * @since 4.9.0 Deprecated $levels and added $options. 124 * @since 4.9.0 Returns an iterator object instead of an array. 123 125 * 124 * @param string $folder Optional. Full path to folder. Default empty. 125 * @param int $levels Optional. Levels of folders to follow, Default 100 (PHP Loop limit). 126 * @return bool|array False on failure, Else array of files 126 * @param string $folder Optional. Full path to folder. Default empty. 127 * @param int $levels Deprecated. 128 * @param array $options { 129 * Associative array of options for the iterator. 130 * 131 * @type array $exclusions Files or folders to exclude. 132 * @type string $regex Regular expression the filename must match. 133 * @type bool $skip_hidden Whether to skip hidden files and folders. 134 * } 135 * @return Iterator|false Iterator on filtered files, or false on failure. 127 136 */ 128 function list_files( $folder = '', $levels = 100 ) {129 if ( empty( $folder) )137 function list_files( $folder = '', $levels = 100, $options = array() ) { 138 if ( empty( $folder ) ) { 130 139 return false; 140 } 131 141 132 if ( ! $levels ) 133 return false; 142 $options = wp_parse_args( $options, array( 143 'exclusions' => array(), 144 'regex' => false, 145 'skip_hidden' => true, 146 ) ); 134 147 135 $files = array(); 136 if ( $dir = @opendir( $folder ) ) { 137 while (($file = readdir( $dir ) ) !== false ) { 138 if ( in_array($file, array('.', '..') ) ) 139 continue; 140 if ( is_dir( $folder . '/' . $file ) ) { 141 $files2 = list_files( $folder . '/' . $file, $levels - 1); 142 if ( $files2 ) 143 $files = array_merge($files, $files2 ); 144 else 145 $files[] = $folder . '/' . $file . '/'; 146 } else { 147 $files[] = $folder . '/' . $file; 148 } 149 } 148 if ( ! class_exists( 'WP_ListFiles_FilterIterator' ) ) { 149 require ABSPATH . WPINC . '/class-wp-list-files-filter-iterator.php'; 150 } 151 152 $iterator = new RecursiveDirectoryIterator( $folder ); 153 $iterator = new WP_ListFiles_FilterIterator( $iterator, $options ); 154 155 if ( $options['regex'] ) { 156 $iterator = new RecursiveRegexIterator( 157 $iterator, 158 $options['regex'], 159 RecursiveRegexIterator::MATCH 160 ); 150 161 } 151 @closedir( $dir ); 152 return $files;162 163 return new RecursiveIteratorIterator( $iterator ); 153 164 } 154 165 155 166 /** -
src/wp-admin/includes/plugin.php
diff --git src/wp-admin/includes/plugin.php src/wp-admin/includes/plugin.php index 69aeacda2f..f54b662d16 100644
function _get_plugin_data_markup_translate( $plugin_file, $plugin_data, $markup 190 190 * @param string $plugin Path to the main plugin file from plugins directory. 191 191 * @return array List of files relative to the plugin root. 192 192 */ 193 function get_plugin_files( $plugin) {193 function get_plugin_files( $plugin ) { 194 194 $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 $data = get_plugin_data( $plugin_file ); 197 198 $label = isset( $data['Version'] ) 199 ? 'list_files_cache_' . $dir . '-' . $data['Version'] 200 : 'list_files_cache_' . $dir; 201 $transient_key = substr( $label, 0, 29 ) . md5( $label ); 202 203 $plugin_files = get_transient( $transient_key ); 204 if ( false !== $plugin_files ) { 205 return $plugin_files; 220 206 } 221 207 208 $plugin_files = array( $plugin ); 209 if ( is_dir( $dir ) && WP_PLUGIN_DIR !== $dir ) { 210 211 /** 212 * Filters the array of excluded directories and files while scanning 213 * for plugin files. 214 * 215 * @since 4.9 216 * 217 * @param array $exclusions Array of excluded directories and files. 218 */ 219 $exclusions = (array) apply_filters( 220 'get_plugin_files_exclusions', 221 array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) 222 ); 223 224 $plugin_files = list_files( $dir, $exclusions ); 225 } 226 227 set_transient( $transient_key, $plugin_files, HOUR_IN_SECONDS ); 228 222 229 return $plugin_files; 223 230 } 224 231 -
src/wp-admin/theme-editor.php
diff --git src/wp-admin/theme-editor.php src/wp-admin/theme-editor.php index b49013ffd8..97eb6ddaad 100644
$file_types = wp_get_theme_file_editable_extensions( $theme ); 75 75 foreach ( $file_types as $type ) { 76 76 switch ( $type ) { 77 77 case 'php': 78 $allowed_files += $theme->get_files( 'php', 1 );78 $allowed_files += $theme->get_files( 'php', -1 ); 79 79 $has_templates = ! empty( $allowed_files ); 80 80 break; 81 81 case 'css': 82 $style_files = $theme->get_files( 'css' );82 $style_files = $theme->get_files( 'css', -1 ); 83 83 $allowed_files['style.css'] = $style_files['style.css']; 84 84 $allowed_files += $style_files; 85 85 break; 86 86 default: 87 $allowed_files += $theme->get_files( $type );87 $allowed_files += $theme->get_files( $type, -1 ); 88 88 break; 89 89 } 90 90 } -
new file src/wp-includes/class-wp-list-files-filter-iterator.php
diff --git src/wp-includes/class-wp-list-files-filter-iterator.php src/wp-includes/class-wp-list-files-filter-iterator.php new file mode 100644 index 0000000000..0454d91dd9
- + 1 <?php 2 /** 3 * ListFiles FilterIterator class. 4 * 5 * Used internally by the `list_files()` function to allow filtering of the 6 * recursive iterator. 7 * 8 * @package WordPress 9 */ 10 11 /** 12 * Helper class to allow filtering of the `list_files()` recursive iterator. 13 * 14 * @since 4.9.0 15 */ 16 final class WP_ListFiles_FilterIterator extends RecursiveFilterIterator { 17 18 const EXCLUSIONS = 'exclusions'; 19 const SKIP_HIDDEN = 'skip_hidden'; 20 21 private $exclusions = array(); 22 private $skip_hidden = true; 23 24 /** 25 * Instantiate a WP_ListFiles_FilterIterator object. 26 * 27 * @since 4.9.0 28 * 29 * @param RecursiveIterator $iterator Iterator to create a filter on. 30 * @param array $options { 31 * Optional. Associative array of options for the iterator. 32 * 33 * @type array $exclusions Files or folders to exclude. 34 * @type bool $skip_hidden Whether to skip hidden files and folders. 35 * } 36 */ 37 public function __construct( RecursiveIterator $iterator, $options ) { 38 $this->parse_options( $options ); 39 parent::__construct( $iterator ); 40 } 41 42 /** 43 * Parse the options that were passed in and store them in properties. 44 * 45 * @since 4.9.0 46 * 47 * @param array $options Associative array of options to parse. 48 */ 49 private function parse_options( $options ) { 50 $keys = array( self::EXCLUSIONS, self::SKIP_HIDDEN ); 51 foreach( $keys as $key ) { 52 $this->$key = $options[ $key ]; 53 } 54 } 55 56 /** 57 * Check whether the current element of the iterator is acceptable. 58 * 59 * @since 4.9.0 60 * 61 * @return bool Whether the current element is acceptable. 62 */ 63 public function accept() { 64 $name = $this->current()->getFilename(); 65 66 // Skip hidden files and folders depending on options. 67 if ( $this->skip_hidden && '.' === $name[0] ) { 68 return false; 69 } 70 71 // Skip files and folders that figure on the list of exclusions. 72 if ( ! empty( $this->exclusions ) 73 && in_array( $name, $this->exclusions, true ) ) { 74 return false; 75 } 76 77 // Accept everything else. 78 return true; 79 } 80 } -
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..70c0fe2be8 100644
final class WP_Theme implements ArrayAccess { 981 981 * @param int $depth Optional. How deep to search for files. Defaults to a flat scan (0 depth). -1 depth is infinite. 982 982 * @param bool $search_parent Optional. Whether to return parent files. Defaults to false. 983 983 * @return array Array of files, keyed by the path to the file relative to the theme's directory, with the values 984 * 984 * being absolute paths. 985 985 */ 986 986 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 = 'list_files_cache_' . $this->get( 'Name' ) . '-' . $this->get( 'Version' ); 988 989 989 if ( $search_parent && $this->parent() ) 990 $files += (array) self::scandir( $this->get_template_directory(), $type, $depth ); 990 $transient_key = substr( $label, 0, 29 ) . md5( $label ); 991 992 $all_files = get_transient( $transient_key ); 993 if ( false === $all_files ) { 994 $all_files = (array) self::scandir( $this->get_stylesheet_directory(), null, -1 ); 995 996 if ( $search_parent && $this->parent() ) { 997 $all_files += (array) self::scandir( $this->get_template_directory(), null, -1 ); 998 } 999 1000 set_transient( $transient_key, $all_files, HOUR_IN_SECONDS ); 1001 } 1002 1003 // Filter $all_files by $type & $depth. 1004 $files = array(); 1005 if ( $type ) { 1006 $type = (array) $type; 1007 $_extensions = implode( '|', $type ); 1008 } 1009 foreach ( $all_files as $key => $file ) { 1010 if ( $depth >= 0 && substr_count( $key, '/' ) > $depth ) { 1011 continue; // Filter by depth. 1012 } 1013 if ( ! $type || preg_match( '~\.(' . $_extensions . ')$~', $file ) ) { // Filter by type. 1014 $files[ $key ] = $file; 1015 } 1016 } 991 1017 992 1018 return $files; 993 1019 } … … final class WP_Theme implements ArrayAccess { 1107 1133 * with `$relative_path`, with the values being absolute paths. False otherwise. 1108 1134 */ 1109 1135 private static function scandir( $path, $extensions = null, $depth = 0, $relative_path = '' ) { 1110 if ( ! is_dir( $path ) ) 1136 if ( ! is_dir( $path ) ) { 1111 1137 return false; 1138 } 1112 1139 1113 1140 if ( $extensions ) { 1114 1141 $extensions = (array) $extensions; … … final class WP_Theme implements ArrayAccess { 1116 1143 } 1117 1144 1118 1145 $relative_path = trailingslashit( $relative_path ); 1119 if ( '/' == $relative_path ) 1146 if ( '/' == $relative_path ) { 1120 1147 $relative_path = ''; 1148 } 1121 1149 1122 1150 $results = scandir( $path ); 1123 1151 $files = array(); … … final class WP_Theme implements ArrayAccess { 1125 1153 /** 1126 1154 * Filters the array of excluded directories and files while scanning theme folder. 1127 1155 * 1128 1156 * @since 4.7.4 1129 1157 * 1130 1158 * @param array $exclusions Array of excluded directories and files. 1131 1159 */ 1132 $exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules' ) );1160 $exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) ); 1133 1161 1134 1162 foreach ( $results as $result ) { 1135 1163 if ( '.' == $result[0] || in_array( $result, $exclusions, true ) ) { 1136 1164 continue; 1137 1165 } 1138 1166 if ( is_dir( $path . '/' . $result ) ) { 1139 if ( ! $depth ) 1167 if ( ! $depth ) { 1140 1168 continue; 1169 } 1141 1170 $found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result ); 1142 1171 $files = array_merge_recursive( $files, $found ); 1143 1172 } elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) { -
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..645a351e9b
- + 1 <?php 2 3 /** 4 * Test list_files(). 5 * 6 * @group functions.php 7 */ 8 class Tests_Functions_ListFiles extends WP_UnitTestCase { 9 public function test_list_files_returns_iterator() { 10 $admin_files = list_files( ABSPATH . 'wp-admin/' ); 11 $this->assertInstanceOf( 'RecursiveIteratorIterator', $admin_files ); 12 $admin_files->rewind(); 13 $file = $admin_files->current(); 14 $this->assertInstanceOf( 'SplFileInfo', $file ); 15 } 16 17 public function test_list_files_returns_spl_file_info_objects() { 18 $admin_files = list_files( ABSPATH . 'wp-admin/' ); 19 $admin_files->rewind(); 20 $file = $admin_files->current(); 21 $this->assertInstanceOf( 'SplFileInfo', $file ); 22 $this->assertContains( '/src/wp-admin/about.php', $file->getPathName() ); 23 } 24 25 public function test_list_files_can_be_cast_to_string() { 26 $admin_files = list_files( ABSPATH . 'wp-admin/' ); 27 $admin_files->rewind(); 28 $file = $admin_files->current(); 29 $file = (string) $file; 30 $this->assertInternalType( 'string', $file ); 31 $this->assertContains( '/src/wp-admin/about.php', $file ); 32 } 33 34 public function test_list_files_can_exclude_files() { 35 $options = array( 36 'exclusions' => array( 'about.php' ), 37 ); 38 $admin_files = list_files( ABSPATH . 'wp-admin/', null, $options ); 39 $admin_files->rewind(); 40 $file = $admin_files->current(); 41 $this->assertNotContains( '/src/wp-admin/about.php', $file->getPathName() ); 42 } 43 44 public function test_list_files_can_match_regex() { 45 $options = array( 46 'regex' => '/.*\/options\.php$/i', 47 ); 48 $admin_files = list_files( ABSPATH . 'wp-admin/', null, $options ); 49 $admin_files->rewind(); 50 $file = $admin_files->current(); 51 $this->assertContains( '/src/wp-admin/options.php', $file->getPathName() ); 52 } 53 }