* WordPress Direct Filesystem.
* WordPress Filesystem Class for direct PHP file and folder manipulation.
* @see WP_Filesystem_Base
class WP_Filesystem_Direct extends WP_Filesystem_Base {
* @param mixed $arg Not used.
public function __construct( $arg ) {
$this->method = 'direct';
$this->errors = new WP_Error();
* Reads entire file into a string.
* @param string $file Name of the file to read.
* @return string|false Read data on success, false on failure.
public function get_contents( $file ) {
return @file_get_contents( $file );
* Reads entire file into an array.
* @param string $file Path to the file.
* @return array|false File contents in an array on success, false on failure.
public function get_contents_array( $file ) {
* Writes a string to a file.
* @param string $file Remote path to the file where to write the data.
* @param string $contents The data to write.
* @param int|false $mode Optional. The file permissions as octal number, usually 0644.
* @return bool True on success, false on failure.
public function put_contents( $file, $contents, $mode = false ) {
$fp = @fopen( $file, 'wb' );
mbstring_binary_safe_encoding();
$data_length = strlen( $contents );
$bytes_written = fwrite( $fp, $contents );
reset_mbstring_encoding();
if ( $data_length !== $bytes_written ) {
$this->chmod( $file, $mode );
* Gets the current working directory.
* @return string|false The current working directory on success, false on failure.
* Changes current directory.
* @param string $dir The new current directory.
* @return bool True on success, false on failure.
public function chdir( $dir ) {
* Changes the file group.
* @param string $file Path to the file.
* @param string|int $group A group name or number.
* @param bool $recursive Optional. If set to true, changes file group recursively.
* @return bool True on success, false on failure.
public function chgrp( $file, $group, $recursive = false ) {
if ( ! $this->exists( $file ) ) {
return chgrp( $file, $group );
if ( ! $this->is_dir( $file ) ) {
return chgrp( $file, $group );
// Is a directory, and we want recursive.
$file = trailingslashit( $file );
$filelist = $this->dirlist( $file );
foreach ( $filelist as $filename ) {
$this->chgrp( $file . $filename, $group, $recursive );
* Changes filesystem permissions.
* @param string $file Path to the file.
* @param int|false $mode Optional. The permissions as octal number, usually 0644 for files,
* 0755 for directories. Default false.
* @param bool $recursive Optional. If set to true, changes file permissions recursively.
* @return bool True on success, false on failure.
public function chmod( $file, $mode = false, $recursive = false ) {
if ( $this->is_file( $file ) ) {
} elseif ( $this->is_dir( $file ) ) {
if ( ! $recursive || ! $this->is_dir( $file ) ) {
return chmod( $file, $mode );
// Is a directory, and we want recursive.
$file = trailingslashit( $file );
$filelist = $this->dirlist( $file );
foreach ( (array) $filelist as $filename => $filemeta ) {
$this->chmod( $file . $filename, $mode, $recursive );
* Changes the owner of a file or directory.
* @param string $file Path to the file or directory.
* @param string|int $owner A user name or number.
* @param bool $recursive Optional. If set to true, changes file owner recursively.
* @return bool True on success, false on failure.
public function chown( $file, $owner, $recursive = false ) {
if ( ! $this->exists( $file ) ) {
return chown( $file, $owner );
if ( ! $this->is_dir( $file ) ) {
return chown( $file, $owner );
// Is a directory, and we want recursive.
$filelist = $this->dirlist( $file );
foreach ( $filelist as $filename ) {
$this->chown( $file . '/' . $filename, $owner, $recursive );
* @param string $file Path to the file.
* @return string|false Username of the owner on success, false on failure.
public function owner( $file ) {
$owneruid = @fileowner( $file );
if ( ! function_exists( 'posix_getpwuid' ) ) {
$ownerarray = posix_getpwuid( $owneruid );
return $ownerarray['name'];
* Gets the permissions of the specified file or filepath in their octal format.
* FIXME does not handle errors in fileperms()
* @param string $file Path to the file.
* @return string Mode of the file (the last 3 digits).
public function getchmod( $file ) {
return substr( decoct( @fileperms( $file ) ), -3 );
* @param string $file Path to the file.
* @return string|false The group on success, false on failure.
public function group( $file ) {
$gid = @filegroup( $file );
if ( ! function_exists( 'posix_getgrgid' ) ) {
$grouparray = posix_getgrgid( $gid );
return $grouparray['name'];
* @param string $source Path to the source file.
* @param string $destination Path to the destination file.
* @param bool $overwrite Optional. Whether to overwrite the destination file if it exists.
* @param int|false $mode Optional. The permissions as octal number, usually 0644 for files,
* 0755 for dirs. Default false.
* @return bool True on success, false on failure.
public function copy( $source, $destination, $overwrite = false, $mode = false ) {
if ( ! $overwrite && $this->exists( $destination ) ) {
$rtval = copy( $source, $destination );
$this->chmod( $destination, $mode );
* 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()`.
* @param string $source Path to the source file.
* @param string $destination Path to the destination file.
* @param bool $overwrite Optional. Whether to overwrite the destination file if it exists.
* @return bool True on success, false on failure.
public function move( $source, $destination, $overwrite = false ) {
if ( ! $overwrite && $this->exists( $destination ) ) {
if ( $overwrite && $this->exists( $destination ) && ! $this->delete( $destination, true ) ) {
// Can't overwrite if the destination couldn't be deleted.
// Try using rename first. if that fails (for example, source is read only) try copy.
if ( @rename( $source, $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 );
* Deletes a file or directory.
* @param string $file Path to the file or directory.
* @param bool $recursive Optional. If set to true, deletes files and folders recursively.
* @param string|false $type Type of resource. 'f' for file, 'd' for directory.
* @return bool True on success, false on failure.
public function delete( $file, $recursive = false, $type = false ) {
// Some filesystems report this as /, which can cause non-expected recursive deletion of all files in the filesystem.
$file = str_replace( '\\', '/', $file ); // For Win32, occasional problems deleting files otherwise.
if ( 'f' === $type || $this->is_file( $file ) ) {
if ( ! $recursive && $this->is_dir( $file ) ) {
// At this point it's a folder, and we're in recursive mode.
$file = trailingslashit( $file );
$filelist = $this->dirlist( $file, true );
if ( is_array( $filelist ) ) {
foreach ( $filelist as $filename => $fileinfo ) {
if ( ! $this->delete( $file . $filename, $recursive, $fileinfo['type'] ) ) {
if ( file_exists( $file ) && ! @rmdir( $file ) ) {
* Checks if a file or directory exists.
* @param string $path Path to file or directory.
* @return bool Whether $path exists or not.
public function exists( $path ) {
return @file_exists( $path );
* Checks if resource is a file.
* @param string $file File path.
* @return bool Whether $file is a file.
public function is_file( $file ) {
return @is_file( $file );
* Checks if resource is a directory.
* @param string $path Directory path.
* @return bool Whether $path is a directory.
public function is_dir( $path ) {
* Checks if a file is readable.
* @param string $file Path to file.
* @return bool Whether $file is readable.
public function is_readable( $file ) {
return @is_readable( $file );
* Checks if a file or directory is writable.
* @param string $path Path to file or directory.
* @return bool Whether $path is writable.
public function is_writable( $path ) {
return @is_writable( $path );
* Gets the file's last access time.
* @param string $file Path to file.
* @return int|false Unix timestamp representing last access time, false on failure.
public function atime( $file ) {
return @fileatime( $file );
* Gets the file modification time.
* @param string $file Path to file.
* @return int|false Unix timestamp representing modification time, false on failure.
public function mtime( $file ) {
return @filemtime( $file );
* Gets the file size (in bytes).
* @param string $file Path to file.
* @return int|false Size of the file in bytes on success, false on failure.