[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-admin/includes/ -> class-wp-filesystem-direct.php (source)

   1  <?php
   2  /**
   3   * WordPress Direct Filesystem.
   4   *
   5   * @package WordPress
   6   * @subpackage Filesystem
   7   */
   8  
   9  /**
  10   * WordPress Filesystem Class for direct PHP file and folder manipulation.
  11   *
  12   * @since 2.5.0
  13   *
  14   * @see WP_Filesystem_Base
  15   * @phpstan-import-type FileListing from WP_Filesystem_Base
  16   */
  17  class WP_Filesystem_Direct extends WP_Filesystem_Base {
  18  
  19      /**
  20       * Constructor.
  21       *
  22       * @since 2.5.0
  23       *
  24       * @param mixed $arg Not used.
  25       */
  26  	public function __construct( $arg ) {
  27          // The $arg parameter is required for signature parity with the other transports, but is unused here.
  28          unset( $arg );
  29          $this->method = 'direct';
  30          $this->errors = new WP_Error();
  31      }
  32  
  33      /**
  34       * Reads entire file into a string.
  35       *
  36       * @since 2.5.0
  37       *
  38       * @param string $file Name of the file to read.
  39       * @return string|false Read data on success, false on failure.
  40       */
  41  	public function get_contents( $file ) {
  42          return @file_get_contents( $file );
  43      }
  44  
  45      /**
  46       * Reads entire file into an array.
  47       *
  48       * @since 2.5.0
  49       *
  50       * @param string $file Path to the file.
  51       * @return string[]|false File contents in an array on success, false on failure.
  52       */
  53  	public function get_contents_array( $file ) {
  54          return @file( $file );
  55      }
  56  
  57      /**
  58       * Writes a string to a file.
  59       *
  60       * @since 2.5.0
  61       *
  62       * @param string    $file     Remote path to the file where to write the data.
  63       * @param string    $contents The data to write.
  64       * @param int|false $mode     Optional. The file permissions as octal number, usually 0644.
  65       *                            Default false.
  66       * @return bool True on success, false on failure.
  67       */
  68  	public function put_contents( $file, $contents, $mode = false ) {
  69          $fp = @fopen( $file, 'wb' );
  70  
  71          if ( ! $fp ) {
  72              return false;
  73          }
  74  
  75          mbstring_binary_safe_encoding();
  76  
  77          $data_length = strlen( $contents );
  78  
  79          $bytes_written = fwrite( $fp, $contents );
  80  
  81          reset_mbstring_encoding();
  82  
  83          fclose( $fp );
  84  
  85          if ( $data_length !== $bytes_written ) {
  86              return false;
  87          }
  88  
  89          $this->chmod( $file, $mode );
  90  
  91          return true;
  92      }
  93  
  94      /**
  95       * Gets the current working directory.
  96       *
  97       * @since 2.5.0
  98       *
  99       * @return string|false The current working directory on success, false on failure.
 100       */
 101  	public function cwd() {
 102          return getcwd();
 103      }
 104  
 105      /**
 106       * Changes current directory.
 107       *
 108       * @since 2.5.0
 109       *
 110       * @param string $dir The new current directory.
 111       * @return bool True on success, false on failure.
 112       */
 113  	public function chdir( $dir ) {
 114          return @chdir( $dir );
 115      }
 116  
 117      /**
 118       * Changes the file group.
 119       *
 120       * @since 2.5.0
 121       *
 122       * @param string     $file      Path to the file.
 123       * @param string|int $group     A group name or number.
 124       * @param bool       $recursive Optional. If set to true, changes file group recursively.
 125       *                              Default false.
 126       * @return bool True on success, false on failure.
 127       */
 128  	public function chgrp( $file, $group, $recursive = false ) {
 129          if ( ! $this->exists( $file ) ) {
 130              return false;
 131          }
 132  
 133          if ( ! $recursive ) {
 134              return chgrp( $file, $group );
 135          }
 136  
 137          if ( ! $this->is_dir( $file ) ) {
 138              return chgrp( $file, $group );
 139          }
 140  
 141          // Is a directory, and we want recursive.
 142          $file     = trailingslashit( $file );
 143          $filelist = $this->dirlist( $file );
 144          if ( false === $filelist ) {
 145              return false;
 146          }
 147  
 148          foreach ( $filelist as $file_listing ) {
 149              $this->chgrp( $file . $file_listing['name'], $group, $recursive );
 150          }
 151  
 152          return true;
 153      }
 154  
 155      /**
 156       * Changes filesystem permissions.
 157       *
 158       * @since 2.5.0
 159       *
 160       * @param string    $file      Path to the file.
 161       * @param int|false $mode      Optional. The permissions as octal number, usually 0644 for files,
 162       *                             0755 for directories. Default false.
 163       * @param bool      $recursive Optional. If set to true, changes file permissions recursively.
 164       *                             Default false.
 165       * @return bool True on success, false on failure.
 166       */
 167  	public function chmod( $file, $mode = false, $recursive = false ) {
 168          if ( ! $mode ) {
 169              if ( $this->is_file( $file ) ) {
 170                  $mode = FS_CHMOD_FILE;
 171              } elseif ( $this->is_dir( $file ) ) {
 172                  $mode = FS_CHMOD_DIR;
 173              } else {
 174                  return false;
 175              }
 176          }
 177  
 178          if ( ! $recursive || ! $this->is_dir( $file ) ) {
 179              $current_mode = fileperms( $file ) & 0777 | 0644;
 180  
 181              /*
 182               * fileperms() populates the stat cache, so have to clear it
 183               * to maintain parity with the previous behavior.
 184               */
 185              clearstatcache( true, $file );
 186  
 187              /*
 188               * Avoid calling chmod() if the requested mode is already set,
 189               * to prevent throwing a warning when we aren't the owner.
 190               */
 191              if ( $current_mode === $mode ) {
 192                  return true;
 193              }
 194  
 195              return chmod( $file, $mode );
 196          }
 197  
 198          // Is a directory, and we want recursive.
 199          $file     = trailingslashit( $file );
 200          $filelist = $this->dirlist( $file );
 201  
 202          foreach ( (array) $filelist as $filename => $filemeta ) {
 203              $this->chmod( $file . $filename, $mode, $recursive );
 204          }
 205  
 206          return true;
 207      }
 208  
 209      /**
 210       * Changes the owner of a file or directory.
 211       *
 212       * @since 2.5.0
 213       *
 214       * @param string     $file      Path to the file or directory.
 215       * @param string|int $owner     A user name or number.
 216       * @param bool       $recursive Optional. If set to true, changes file owner recursively.
 217       *                              Default false.
 218       * @return bool True on success, false on failure.
 219       */
 220  	public function chown( $file, $owner, $recursive = false ) {
 221          if ( ! $this->exists( $file ) ) {
 222              return false;
 223          }
 224  
 225          if ( ! $recursive ) {
 226              return chown( $file, $owner );
 227          }
 228  
 229          if ( ! $this->is_dir( $file ) ) {
 230              return chown( $file, $owner );
 231          }
 232  
 233          // Is a directory, and we want recursive.
 234          $filelist = $this->dirlist( $file );
 235          if ( false === $filelist ) {
 236              return false;
 237          }
 238  
 239          foreach ( $filelist as $file_listing ) {
 240              $this->chown( $file . '/' . $file_listing['name'], $owner, $recursive );
 241          }
 242  
 243          return true;
 244      }
 245  
 246      /**
 247       * Gets the file owner.
 248       *
 249       * @since 2.5.0
 250       *
 251       * @param string $file Path to the file.
 252       * @return string|int<1, max>|false Username of the owner on success, or UID of file owner if not available; false on failure.
 253       */
 254  	public function owner( $file ) {
 255          $owneruid = @fileowner( $file );
 256  
 257          if ( ! $owneruid ) {
 258              return false;
 259          }
 260  
 261          if ( ! function_exists( 'posix_getpwuid' ) ) {
 262              return $owneruid;
 263          }
 264  
 265          $ownerarray = posix_getpwuid( $owneruid );
 266  
 267          if ( ! $ownerarray ) {
 268              return false;
 269          }
 270  
 271          return $ownerarray['name'];
 272      }
 273  
 274      /**
 275       * Gets the permissions of the specified file or filepath in their octal format.
 276       *
 277       * @since 2.5.0
 278       *
 279       * @param string $file Path to the file.
 280       * @return string Mode of the file (the last 3 digits), or the string "0" on failure.
 281       */
 282  	public function getchmod( $file ) {
 283          $perms = @fileperms( $file );
 284          if ( false === $perms ) {
 285              return '0';
 286          }
 287  
 288          return substr( decoct( $perms ), -3 );
 289      }
 290  
 291      /**
 292       * Gets the file's group.
 293       *
 294       * @since 2.5.0
 295       *
 296       * @param string $file Path to the file.
 297       * @return string|int<1, max>|false Group name on success, or GID of the file's group if not available; false on failure.
 298       */
 299  	public function group( $file ) {
 300          $gid = @filegroup( $file );
 301  
 302          if ( ! $gid ) {
 303              return false;
 304          }
 305  
 306          if ( ! function_exists( 'posix_getgrgid' ) ) {
 307              return $gid;
 308          }
 309  
 310          $grouparray = posix_getgrgid( $gid );
 311  
 312          if ( ! $grouparray ) {
 313              return false;
 314          }
 315  
 316          return $grouparray['name'];
 317      }
 318  
 319      /**
 320       * Copies a file.
 321       *
 322       * @since 2.5.0
 323       *
 324       * @param string    $source      Path to the source file.
 325       * @param string    $destination Path to the destination file.
 326       * @param bool      $overwrite   Optional. Whether to overwrite the destination file if it exists.
 327       *                               Default false.
 328       * @param int|false $mode        Optional. The permissions as octal number, usually 0644 for files,
 329       *                               0755 for dirs. Default false.
 330       * @return bool True on success, false on failure.
 331       */
 332  	public function copy( $source, $destination, $overwrite = false, $mode = false ) {
 333          if ( ! $overwrite && $this->exists( $destination ) ) {
 334              return false;
 335          }
 336  
 337          $rtval = copy( $source, $destination );
 338  
 339          if ( $mode ) {
 340              $this->chmod( $destination, $mode );
 341          }
 342  
 343          return $rtval;
 344      }
 345  
 346      /**
 347       * Moves a file or directory.
 348       *
 349       * After moving files or directories, OPcache will need to be invalidated.
 350       *
 351       * If moving a directory fails, `copy_dir()` can be used for a recursive copy.
 352       *
 353       * Use `move_dir()` for moving directories with OPcache invalidation and a
 354       * fallback to `copy_dir()`.
 355       *
 356       * @since 2.5.0
 357       *
 358       * @param string $source      Path to the source file.
 359       * @param string $destination Path to the destination file.
 360       * @param bool   $overwrite   Optional. Whether to overwrite the destination file if it exists.
 361       *                            Default false.
 362       * @return bool True on success, false on failure.
 363       */
 364  	public function move( $source, $destination, $overwrite = false ) {
 365          if ( ! $overwrite && $this->exists( $destination ) ) {
 366              return false;
 367          }
 368  
 369          if ( $overwrite && $this->exists( $destination ) && ! $this->delete( $destination, true ) ) {
 370              // Can't overwrite if the destination couldn't be deleted.
 371              return false;
 372          }
 373  
 374          // Try using rename first. if that fails (for example, source is read only) try copy.
 375          if ( @rename( $source, $destination ) ) {
 376              return true;
 377          }
 378  
 379          // Backward compatibility: Only fall back to `::copy()` for single files.
 380          if ( $this->is_file( $source ) && $this->copy( $source, $destination, $overwrite ) && $this->exists( $destination ) ) {
 381              $this->delete( $source );
 382  
 383              return true;
 384          } else {
 385              return false;
 386          }
 387      }
 388  
 389      /**
 390       * Deletes a file or directory.
 391       *
 392       * @since 2.5.0
 393       *
 394       * @param string       $file      Path to the file or directory.
 395       * @param bool         $recursive Optional. If set to true, deletes files and folders recursively.
 396       *                                Default false.
 397       * @param string|false $type      Type of resource. 'f' for file, 'd' for directory.
 398       *                                Default false.
 399       * @return bool True on success, false on failure.
 400       */
 401  	public function delete( $file, $recursive = false, $type = false ) {
 402          if ( empty( $file ) ) {
 403              // Some filesystems report this as /, which can cause non-expected recursive deletion of all files in the filesystem.
 404              return false;
 405          }
 406  
 407          $file = str_replace( '\\', '/', $file ); // For Win32, occasional problems deleting files otherwise.
 408  
 409          if ( 'f' === $type || $this->is_file( $file ) ) {
 410              return @unlink( $file );
 411          }
 412  
 413          if ( ! $recursive && $this->is_dir( $file ) ) {
 414              return @rmdir( $file );
 415          }
 416  
 417          // At this point it's a folder, and we're in recursive mode.
 418          $file     = trailingslashit( $file );
 419          $filelist = $this->dirlist( $file, true );
 420  
 421          $retval = true;
 422  
 423          if ( is_array( $filelist ) ) {
 424              foreach ( $filelist as $filename => $fileinfo ) {
 425                  if ( ! $this->delete( $file . $filename, $recursive, $fileinfo['type'] ) ) {
 426                      $retval = false;
 427                  }
 428              }
 429          }
 430  
 431          if ( file_exists( $file ) && ! @rmdir( $file ) ) {
 432              $retval = false;
 433          }
 434  
 435          return $retval;
 436      }
 437  
 438      /**
 439       * Checks if a file or directory exists.
 440       *
 441       * @since 2.5.0
 442       *
 443       * @param string $path Path to file or directory.
 444       * @return bool Whether $path exists or not.
 445       */
 446  	public function exists( $path ) {
 447          return @file_exists( $path );
 448      }
 449  
 450      /**
 451       * Checks if resource is a file.
 452       *
 453       * @since 2.5.0
 454       *
 455       * @param string $file File path.
 456       * @return bool Whether $file is a file.
 457       */
 458  	public function is_file( $file ) {
 459          return @is_file( $file );
 460      }
 461  
 462      /**
 463       * Checks if resource is a directory.
 464       *
 465       * @since 2.5.0
 466       *
 467       * @param string $path Directory path.
 468       * @return bool Whether $path is a directory.
 469       */
 470  	public function is_dir( $path ) {
 471          return @is_dir( $path );
 472      }
 473  
 474      /**
 475       * Checks if a file is readable.
 476       *
 477       * @since 2.5.0
 478       *
 479       * @param string $file Path to file.
 480       * @return bool Whether $file is readable.
 481       */
 482  	public function is_readable( $file ) {
 483          return @is_readable( $file );
 484      }
 485  
 486      /**
 487       * Checks if a file or directory is writable.
 488       *
 489       * @since 2.5.0
 490       *
 491       * @param string $path Path to file or directory.
 492       * @return bool Whether $path is writable.
 493       */
 494  	public function is_writable( $path ) {
 495          return @is_writable( $path );
 496      }
 497  
 498      /**
 499       * Gets the file's last access time.
 500       *
 501       * @since 2.5.0
 502       *
 503       * @param string $file Path to file.
 504       * @return int|false Unix timestamp representing last access time, false on failure.
 505       */
 506  	public function atime( $file ) {
 507          return @fileatime( $file );
 508      }
 509  
 510      /**
 511       * Gets the file modification time.
 512       *
 513       * @since 2.5.0
 514       *
 515       * @param string $file Path to file.
 516       * @return int|false Unix timestamp representing modification time, false on failure.
 517       */
 518  	public function mtime( $file ) {
 519          return @filemtime( $file );
 520      }
 521  
 522      /**
 523       * Gets the file size (in bytes).
 524       *
 525       * @since 2.5.0
 526       *
 527       * @param string $file Path to file.
 528       * @return int|false Size of the file in bytes on success, false on failure.
 529       */
 530  	public function size( $file ) {
 531          return @filesize( $file );
 532      }
 533  
 534      /**
 535       * Sets the access and modification times of a file.
 536       *
 537       * Note: If $file doesn't exist, it will be created.
 538       *
 539       * @since 2.5.0
 540       *
 541       * @param string $file  Path to file.
 542       * @param int    $time  Optional. Modified time to set for file.
 543       *                      Default 0.
 544       * @param int    $atime Optional. Access time to set for file.
 545       *                      Default 0.
 546       * @return bool True on success, false on failure.
 547       */
 548  	public function touch( $file, $time = 0, $atime = 0 ) {
 549          if ( 0 === $time ) {
 550              $time = time();
 551          }
 552  
 553          if ( 0 === $atime ) {
 554              $atime = time();
 555          }
 556  
 557          return touch( $file, $time, $atime );
 558      }
 559  
 560      /**
 561       * Creates a directory.
 562       *
 563       * @since 2.5.0
 564       *
 565       * @param string           $path  Path for new directory.
 566       * @param int|false        $chmod Optional. The permissions as octal number (or false to skip chmod).
 567       *                                Default false.
 568       * @param string|int|false $chown Optional. A user name or number (or false to skip chown).
 569       *                                Default false.
 570       * @param string|int|false $chgrp Optional. A group name or number (or false to skip chgrp).
 571       *                                Default false.
 572       * @return bool True on success, false on failure.
 573       */
 574  	public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) {
 575          // Safe mode fails with a trailing slash under certain PHP versions.
 576          $path = untrailingslashit( $path );
 577  
 578          if ( empty( $path ) ) {
 579              return false;
 580          }
 581  
 582          if ( ! $chmod ) {
 583              $chmod = FS_CHMOD_DIR;
 584          }
 585  
 586          if ( ! @mkdir( $path ) ) {
 587              return false;
 588          }
 589  
 590          $this->chmod( $path, $chmod );
 591  
 592          if ( $chown ) {
 593              $this->chown( $path, $chown );
 594          }
 595  
 596          if ( $chgrp ) {
 597              $this->chgrp( $path, $chgrp );
 598          }
 599  
 600          return true;
 601      }
 602  
 603      /**
 604       * Deletes a directory.
 605       *
 606       * @since 2.5.0
 607       *
 608       * @param string $path      Path to directory.
 609       * @param bool   $recursive Optional. Whether to recursively remove files/directories.
 610       *                          Default false.
 611       * @return bool True on success, false on failure.
 612       */
 613  	public function rmdir( $path, $recursive = false ) {
 614          return $this->delete( $path, $recursive );
 615      }
 616  
 617      /**
 618       * Gets details for files in a directory or a specific file.
 619       *
 620       * @since 2.5.0
 621       *
 622       * @param string $path           Path to directory or file.
 623       * @param bool   $include_hidden Optional. Whether to include details of hidden ("." prefixed) files.
 624       *                               Default true.
 625       * @param bool   $recursive      Optional. Whether to recursively include file details in nested directories.
 626       *                               Default false.
 627       * @return array|false {
 628       *     Array of arrays containing file information. False if unable to list directory contents.
 629       *
 630       *     @type array ...$0 {
 631       *         Array of file information. Note that some elements may not be available on all filesystems.
 632       *
 633       *         @type string           $name        Name of the file or directory.
 634       *         @type string           $perms       *nix representation of permissions.
 635       *         @type string           $permsn      Octal representation of permissions.
 636       *         @type false            $number      File number. Always false in this context.
 637       *         @type string|false     $owner       Owner name or ID, or false if not available.
 638       *         @type string|false     $group       File permissions group, or false if not available.
 639       *         @type int|string|false $size        Size of file in bytes. May be a numeric string.
 640       *                                             False if not available.
 641       *         @type int|string|false $lastmodunix Last modified unix timestamp. May be a numeric string.
 642       *                                             False if not available.
 643       *         @type string|false     $lastmod     Last modified month (3 letters) and day (without leading 0), or
 644       *                                             false if not available.
 645       *         @type string|false     $time        Last modified time, or false if not available.
 646       *         @type string           $type        Type of resource. 'f' for file, 'd' for directory, 'l' for link.
 647       *         @type array|false      $files       If a directory and `$recursive` is true, contains another array of
 648       *                                             files. False if unable to list directory contents.
 649       *     }
 650       * }
 651       * @phpstan-return array<string, FileListing>|false
 652       */
 653  	public function dirlist( $path, $include_hidden = true, $recursive = false ) {
 654          if ( $this->is_file( $path ) ) {
 655              $limit_file = basename( $path );
 656              $path       = dirname( $path );
 657          } else {
 658              $limit_file = false;
 659          }
 660  
 661          if ( ! $this->is_dir( $path ) || ! $this->is_readable( $path ) ) {
 662              return false;
 663          }
 664  
 665          $dir = dir( $path );
 666  
 667          if ( ! $dir ) {
 668              return false;
 669          }
 670  
 671          $path = trailingslashit( $path );
 672          $ret  = array();
 673  
 674          while ( false !== ( $entry = $dir->read() ) ) {
 675              $struc         = array();
 676              $struc['name'] = $entry;
 677  
 678              if ( '.' === $struc['name'] || '..' === $struc['name'] ) {
 679                  continue;
 680              }
 681  
 682              if ( ! $include_hidden && '.' === $struc['name'][0] ) {
 683                  continue;
 684              }
 685  
 686              if ( $limit_file && $struc['name'] !== $limit_file ) {
 687                  continue;
 688              }
 689  
 690              $struc['perms']       = $this->gethchmod( $path . $entry );
 691              $struc['permsn']      = $this->getnumchmodfromh( $struc['perms'] );
 692              $struc['number']      = false;
 693              $struc['owner']       = $this->owner( $path . $entry );
 694              $struc['group']       = $this->group( $path . $entry );
 695              $struc['size']        = $this->size( $path . $entry );
 696              $struc['lastmodunix'] = $this->mtime( $path . $entry );
 697              $struc['lastmod']     = is_int( $struc['lastmodunix'] ) ? gmdate( 'M j', $struc['lastmodunix'] ) : false;
 698              $struc['time']        = is_int( $struc['lastmodunix'] ) ? gmdate( 'h:i:s', $struc['lastmodunix'] ) : false;
 699              $struc['type']        = $this->is_dir( $path . $entry ) ? 'd' : 'f';
 700  
 701              if ( 'd' === $struc['type'] ) {
 702                  if ( $recursive ) {
 703                      $struc['files'] = $this->dirlist( $path . $struc['name'], $include_hidden, $recursive );
 704                  } else {
 705                      $struc['files'] = array();
 706                  }
 707              }
 708  
 709              $ret[ $struc['name'] ] = $struc;
 710          }
 711  
 712          $dir->close();
 713          unset( $dir );
 714  
 715          return $ret;
 716      }
 717  }


Generated : Sun Jul 26 08:20:18 2026 Cross-referenced by PHPXref