diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-admin/includes/class-wp-filesystem-direct.php --- a/wp/wp-admin/includes/class-wp-filesystem-direct.php Thu Sep 29 08:06:27 2022 +0200 +++ b/wp/wp-admin/includes/class-wp-filesystem-direct.php Fri Sep 05 18:40:08 2025 +0200 @@ -316,7 +316,14 @@ } /** - * Moves a file. + * Moves a file or directory. + * + * After moving files or directories, OPcache will need to be invalidated. + * + * If moving a directory fails, `copy_dir()` can be used for a recursive copy. + * + * Use `move_dir()` for moving directories with OPcache invalidation and a + * fallback to `copy_dir()`. * * @since 2.5.0 * @@ -331,12 +338,18 @@ return false; } + if ( $overwrite && $this->exists( $destination ) && ! $this->delete( $destination, true ) ) { + // Can't overwrite if the destination couldn't be deleted. + return false; + } + // Try using rename first. if that fails (for example, source is read only) try copy. if ( @rename( $source, $destination ) ) { return true; } - if ( $this->copy( $source, $destination, $overwrite ) && $this->exists( $destination ) ) { + // Backward compatibility: Only fall back to `::copy()` for single files. + if ( $this->is_file( $source ) && $this->copy( $source, $destination, $overwrite ) && $this->exists( $destination ) ) { $this->delete( $source ); return true; @@ -399,11 +412,11 @@ * * @since 2.5.0 * - * @param string $file Path to file or directory. - * @return bool Whether $file exists or not. + * @param string $path Path to file or directory. + * @return bool Whether $path exists or not. */ - public function exists( $file ) { - return @file_exists( $file ); + public function exists( $path ) { + return @file_exists( $path ); } /** @@ -447,11 +460,11 @@ * * @since 2.5.0 * - * @param string $file Path to file or directory. - * @return bool Whether $file is writable. + * @param string $path Path to file or directory. + * @return bool Whether $path is writable. */ - public function is_writable( $file ) { - return @is_writable( $file ); + public function is_writable( $path ) { + return @is_writable( $path ); } /** @@ -584,18 +597,28 @@ * @param bool $recursive Optional. Whether to recursively include file details in nested directories. * Default false. * @return array|false { - * Array of files. False if unable to list directory contents. + * Array of arrays containing file information. False if unable to list directory contents. + * + * @type array ...$0 { + * Array of file information. Note that some elements may not be available on all filesystems. * - * @type string $name Name of the file or directory. - * @type string $perms *nix representation of permissions. - * @type string $permsn Octal representation of permissions. - * @type string $owner Owner name or ID. - * @type int $size Size of file in bytes. - * @type int $lastmodunix Last modified unix timestamp. - * @type mixed $lastmod Last modified month (3 letter) and day (without leading 0). - * @type int $time Last modified time. - * @type string $type Type of resource. 'f' for file, 'd' for directory. - * @type mixed $files If a directory and `$recursive` is true, contains another array of files. + * @type string $name Name of the file or directory. + * @type string $perms *nix representation of permissions. + * @type string $permsn Octal representation of permissions. + * @type false $number File number. Always false in this context. + * @type string|false $owner Owner name or ID, or false if not available. + * @type string|false $group File permissions group, or false if not available. + * @type int|string|false $size Size of file in bytes. May be a numeric string. + * False if not available. + * @type int|string|false $lastmodunix Last modified unix timestamp. May be a numeric string. + * False if not available. + * @type string|false $lastmod Last modified month (3 letters) and day (without leading 0), or + * false if not available. + * @type string|false $time Last modified time, or false if not available. + * @type string $type Type of resource. 'f' for file, 'd' for directory, 'l' for link. + * @type array|false $files If a directory and `$recursive` is true, contains another array of + * files. False if unable to list directory contents. + * } * } */ public function dirlist( $path, $include_hidden = true, $recursive = false ) { @@ -616,7 +639,8 @@ return false; } - $ret = array(); + $path = trailingslashit( $path ); + $ret = array(); while ( false !== ( $entry = $dir->read() ) ) { $struc = array(); @@ -634,20 +658,20 @@ continue; } - $struc['perms'] = $this->gethchmod( $path . '/' . $entry ); + $struc['perms'] = $this->gethchmod( $path . $entry ); $struc['permsn'] = $this->getnumchmodfromh( $struc['perms'] ); $struc['number'] = false; - $struc['owner'] = $this->owner( $path . '/' . $entry ); - $struc['group'] = $this->group( $path . '/' . $entry ); - $struc['size'] = $this->size( $path . '/' . $entry ); - $struc['lastmodunix'] = $this->mtime( $path . '/' . $entry ); + $struc['owner'] = $this->owner( $path . $entry ); + $struc['group'] = $this->group( $path . $entry ); + $struc['size'] = $this->size( $path . $entry ); + $struc['lastmodunix'] = $this->mtime( $path . $entry ); $struc['lastmod'] = gmdate( 'M j', $struc['lastmodunix'] ); $struc['time'] = gmdate( 'h:i:s', $struc['lastmodunix'] ); - $struc['type'] = $this->is_dir( $path . '/' . $entry ) ? 'd' : 'f'; + $struc['type'] = $this->is_dir( $path . $entry ) ? 'd' : 'f'; if ( 'd' === $struc['type'] ) { if ( $recursive ) { - $struc['files'] = $this->dirlist( $path . '/' . $struc['name'], $include_hidden, $recursive ); + $struc['files'] = $this->dirlist( $path . $struc['name'], $include_hidden, $recursive ); } else { $struc['files'] = array(); }