[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress Filesystem Class for implementing SSH2 4 * 5 * To use this class you must follow these steps for PHP 5.2.6+ 6 * 7 * {@link http://kevin.vanzonneveld.net/techblog/article/make_ssh_connections_with_php/ - Installation Notes} 8 * 9 * Compile libssh2 (Note: Only 0.14 is officially working with PHP 5.2.6+ right now, But many users have found the latest versions work) 10 * 11 * cd /usr/src 12 * wget https://www.libssh2.org/download/libssh2-0.14.tar.gz 13 * tar -zxvf libssh2-0.14.tar.gz 14 * cd libssh2-0.14/ 15 * ./configure 16 * make all install 17 * 18 * Note: Do not leave the directory yet! 19 * 20 * Enter: pecl install -f ssh2 21 * 22 * Copy the ssh.so file it creates to your PHP Module Directory. 23 * Open up your PHP.INI file and look for where extensions are placed. 24 * Add in your PHP.ini file: extension=ssh2.so 25 * 26 * Restart Apache! 27 * Check phpinfo() streams to confirm that: ssh2.shell, ssh2.exec, ssh2.tunnel, ssh2.scp, ssh2.sftp exist. 28 * 29 * Note: As of WordPress 2.8, this utilizes the PHP5+ function `stream_get_contents()`. 30 * 31 * @since 2.7.0 32 * 33 * @package WordPress 34 * @subpackage Filesystem 35 */ 36 class WP_Filesystem_SSH2 extends WP_Filesystem_Base { 37 38 /** 39 * @since 2.7.0 40 * @var resource 41 */ 42 public $link = false; 43 44 /** 45 * @since 2.7.0 46 * @var resource 47 */ 48 public $sftp_link; 49 50 /** 51 * @since 2.7.0 52 * @var bool 53 */ 54 public $keys = false; 55 56 /** 57 * Constructor. 58 * 59 * @since 2.7.0 60 * 61 * @param array $opt 62 */ 63 public function __construct( $opt = '' ) { 64 $this->method = 'ssh2'; 65 $this->errors = new WP_Error(); 66 67 // Check if possible to use ssh2 functions. 68 if ( ! extension_loaded( 'ssh2' ) ) { 69 $this->errors->add( 'no_ssh2_ext', __( 'The ssh2 PHP extension is not available' ) ); 70 return; 71 } 72 73 // Set defaults: 74 if ( empty( $opt['port'] ) ) { 75 $this->options['port'] = 22; 76 } else { 77 $this->options['port'] = $opt['port']; 78 } 79 80 if ( empty( $opt['hostname'] ) ) { 81 $this->errors->add( 'empty_hostname', __( 'SSH2 hostname is required' ) ); 82 } else { 83 $this->options['hostname'] = $opt['hostname']; 84 } 85 86 // Check if the options provided are OK. 87 if ( ! empty( $opt['public_key'] ) && ! empty( $opt['private_key'] ) ) { 88 $this->options['public_key'] = $opt['public_key']; 89 $this->options['private_key'] = $opt['private_key']; 90 91 $this->options['hostkey'] = array( 'hostkey' => 'ssh-rsa,ssh-ed25519' ); 92 93 $this->keys = true; 94 } elseif ( empty( $opt['username'] ) ) { 95 $this->errors->add( 'empty_username', __( 'SSH2 username is required' ) ); 96 } 97 98 if ( ! empty( $opt['username'] ) ) { 99 $this->options['username'] = $opt['username']; 100 } 101 102 if ( empty( $opt['password'] ) ) { 103 // Password can be blank if we are using keys. 104 if ( ! $this->keys ) { 105 $this->errors->add( 'empty_password', __( 'SSH2 password is required' ) ); 106 } else { 107 $this->options['password'] = null; 108 } 109 } else { 110 $this->options['password'] = $opt['password']; 111 } 112 } 113 114 /** 115 * Connects filesystem. 116 * 117 * @since 2.7.0 118 * 119 * @return bool True on success, false on failure. 120 */ 121 public function connect() { 122 if ( ! $this->keys ) { 123 $this->link = @ssh2_connect( $this->options['hostname'], $this->options['port'] ); 124 } else { 125 $this->link = @ssh2_connect( $this->options['hostname'], $this->options['port'], $this->options['hostkey'] ); 126 } 127 128 if ( ! $this->link ) { 129 $this->errors->add( 130 'connect', 131 sprintf( 132 /* translators: %s: hostname:port */ 133 __( 'Failed to connect to SSH2 Server %s' ), 134 $this->options['hostname'] . ':' . $this->options['port'] 135 ) 136 ); 137 138 return false; 139 } 140 141 if ( ! $this->keys ) { 142 if ( ! @ssh2_auth_password( $this->link, $this->options['username'], $this->options['password'] ) ) { 143 $this->errors->add( 144 'auth', 145 sprintf( 146 /* translators: %s: Username. */ 147 __( 'Username/Password incorrect for %s' ), 148 $this->options['username'] 149 ) 150 ); 151 152 return false; 153 } 154 } else { 155 if ( ! @ssh2_auth_pubkey_file( $this->link, $this->options['username'], $this->options['public_key'], $this->options['private_key'], $this->options['password'] ) ) { 156 $this->errors->add( 157 'auth', 158 sprintf( 159 /* translators: %s: Username. */ 160 __( 'Public and Private keys incorrect for %s' ), 161 $this->options['username'] 162 ) 163 ); 164 165 return false; 166 } 167 } 168 169 $this->sftp_link = ssh2_sftp( $this->link ); 170 171 if ( ! $this->sftp_link ) { 172 $this->errors->add( 173 'connect', 174 sprintf( 175 /* translators: %s: hostname:port */ 176 __( 'Failed to initialize a SFTP subsystem session with the SSH2 Server %s' ), 177 $this->options['hostname'] . ':' . $this->options['port'] 178 ) 179 ); 180 181 return false; 182 } 183 184 return true; 185 } 186 187 /** 188 * Gets the ssh2.sftp PHP stream wrapper path to open for the given file. 189 * 190 * This method also works around a PHP bug where the root directory (/) cannot 191 * be opened by PHP functions, causing a false failure. In order to work around 192 * this, the path is converted to /./ which is semantically the same as / 193 * See https://bugs.php.net/bug.php?id=64169 for more details. 194 * 195 * @since 4.4.0 196 * 197 * @param string $path The File/Directory path on the remote server to return 198 * @return string The ssh2.sftp:// wrapped path to use. 199 */ 200 public function sftp_path( $path ) { 201 if ( '/' === $path ) { 202 $path = '/./'; 203 } 204 205 return 'ssh2.sftp://' . $this->sftp_link . '/' . ltrim( $path, '/' ); 206 } 207 208 /** 209 * @since 2.7.0 210 * 211 * @param string $command 212 * @param bool $returnbool 213 * @return bool|string True on success, false on failure. String if the command was executed, `$returnbool` 214 * is false (default), and data from the resulting stream was retrieved. 215 */ 216 public function run_command( $command, $returnbool = false ) { 217 if ( ! $this->link ) { 218 return false; 219 } 220 221 $stream = ssh2_exec( $this->link, $command ); 222 223 if ( ! $stream ) { 224 $this->errors->add( 225 'command', 226 sprintf( 227 /* translators: %s: Command. */ 228 __( 'Unable to perform command: %s' ), 229 $command 230 ) 231 ); 232 } else { 233 stream_set_blocking( $stream, true ); 234 stream_set_timeout( $stream, FS_TIMEOUT ); 235 $data = stream_get_contents( $stream ); 236 fclose( $stream ); 237 238 if ( $returnbool ) { 239 return ( false === $data ) ? false : '' !== trim( $data ); 240 } else { 241 return $data; 242 } 243 } 244 245 return false; 246 } 247 248 /** 249 * Reads entire file into a string. 250 * 251 * @since 2.7.0 252 * 253 * @param string $file Name of the file to read. 254 * @return string|false Read data on success, false if no temporary file could be opened, 255 * or if the file couldn't be retrieved. 256 */ 257 public function get_contents( $file ) { 258 return file_get_contents( $this->sftp_path( $file ) ); 259 } 260 261 /** 262 * Reads entire file into an array. 263 * 264 * @since 2.7.0 265 * 266 * @param string $file Path to the file. 267 * @return array|false File contents in an array on success, false on failure. 268 */ 269 public function get_contents_array( $file ) { 270 return file( $this->sftp_path( $file ) ); 271 } 272 273 /** 274 * Writes a string to a file. 275 * 276 * @since 2.7.0 277 * 278 * @param string $file Remote path to the file where to write the data. 279 * @param string $contents The data to write. 280 * @param int|false $mode Optional. The file permissions as octal number, usually 0644. 281 * Default false. 282 * @return bool True on success, false on failure. 283 */ 284 public function put_contents( $file, $contents, $mode = false ) { 285 $ret = file_put_contents( $this->sftp_path( $file ), $contents ); 286 287 if ( strlen( $contents ) !== $ret ) { 288 return false; 289 } 290 291 $this->chmod( $file, $mode ); 292 293 return true; 294 } 295 296 /** 297 * Gets the current working directory. 298 * 299 * @since 2.7.0 300 * 301 * @return string|false The current working directory on success, false on failure. 302 */ 303 public function cwd() { 304 $cwd = ssh2_sftp_realpath( $this->sftp_link, '.' ); 305 306 if ( $cwd ) { 307 $cwd = trailingslashit( trim( $cwd ) ); 308 } 309 310 return $cwd; 311 } 312 313 /** 314 * Changes current directory. 315 * 316 * @since 2.7.0 317 * 318 * @param string $dir The new current directory. 319 * @return bool True on success, false on failure. 320 */ 321 public function chdir( $dir ) { 322 return $this->run_command( 'cd ' . $dir, true ); 323 } 324 325 /** 326 * Changes the file group. 327 * 328 * @since 2.7.0 329 * 330 * @param string $file Path to the file. 331 * @param string|int $group A group name or number. 332 * @param bool $recursive Optional. If set to true, changes file group recursively. 333 * Default false. 334 * @return bool True on success, false on failure. 335 */ 336 public function chgrp( $file, $group, $recursive = false ) { 337 if ( ! $this->exists( $file ) ) { 338 return false; 339 } 340 341 if ( ! $recursive || ! $this->is_dir( $file ) ) { 342 return $this->run_command( sprintf( 'chgrp %s %s', escapeshellarg( $group ), escapeshellarg( $file ) ), true ); 343 } 344 345 return $this->run_command( sprintf( 'chgrp -R %s %s', escapeshellarg( $group ), escapeshellarg( $file ) ), true ); 346 } 347 348 /** 349 * Changes filesystem permissions. 350 * 351 * @since 2.7.0 352 * 353 * @param string $file Path to the file. 354 * @param int|false $mode Optional. The permissions as octal number, usually 0644 for files, 355 * 0755 for directories. Default false. 356 * @param bool $recursive Optional. If set to true, changes file permissions recursively. 357 * Default false. 358 * @return bool True on success, false on failure. 359 */ 360 public function chmod( $file, $mode = false, $recursive = false ) { 361 if ( ! $this->exists( $file ) ) { 362 return false; 363 } 364 365 if ( ! $mode ) { 366 if ( $this->is_file( $file ) ) { 367 $mode = FS_CHMOD_FILE; 368 } elseif ( $this->is_dir( $file ) ) { 369 $mode = FS_CHMOD_DIR; 370 } else { 371 return false; 372 } 373 } 374 375 if ( ! $recursive || ! $this->is_dir( $file ) ) { 376 return $this->run_command( sprintf( 'chmod %o %s', $mode, escapeshellarg( $file ) ), true ); 377 } 378 379 return $this->run_command( sprintf( 'chmod -R %o %s', $mode, escapeshellarg( $file ) ), true ); 380 } 381 382 /** 383 * Changes the owner of a file or directory. 384 * 385 * @since 2.7.0 386 * 387 * @param string $file Path to the file or directory. 388 * @param string|int $owner A user name or number. 389 * @param bool $recursive Optional. If set to true, changes file owner recursively. 390 * Default false. 391 * @return bool True on success, false on failure. 392 */ 393 public function chown( $file, $owner, $recursive = false ) { 394 if ( ! $this->exists( $file ) ) { 395 return false; 396 } 397 398 if ( ! $recursive || ! $this->is_dir( $file ) ) { 399 return $this->run_command( sprintf( 'chown %s %s', escapeshellarg( $owner ), escapeshellarg( $file ) ), true ); 400 } 401 402 return $this->run_command( sprintf( 'chown -R %s %s', escapeshellarg( $owner ), escapeshellarg( $file ) ), true ); 403 } 404 405 /** 406 * Gets the file owner. 407 * 408 * @since 2.7.0 409 * 410 * @param string $file Path to the file. 411 * @return string|false Username of the owner on success, false on failure. 412 */ 413 public function owner( $file ) { 414 $owneruid = @fileowner( $this->sftp_path( $file ) ); 415 416 if ( ! $owneruid ) { 417 return false; 418 } 419 420 if ( ! function_exists( 'posix_getpwuid' ) ) { 421 return $owneruid; 422 } 423 424 $ownerarray = posix_getpwuid( $owneruid ); 425 426 if ( ! $ownerarray ) { 427 return false; 428 } 429 430 return $ownerarray['name']; 431 } 432 433 /** 434 * Gets the permissions of the specified file or filepath in their octal format. 435 * 436 * @since 2.7.0 437 * 438 * @param string $file Path to the file. 439 * @return string Mode of the file (the last 3 digits). 440 */ 441 public function getchmod( $file ) { 442 return substr( decoct( @fileperms( $this->sftp_path( $file ) ) ), -3 ); 443 } 444 445 /** 446 * Gets the file's group. 447 * 448 * @since 2.7.0 449 * 450 * @param string $file Path to the file. 451 * @return string|false The group on success, false on failure. 452 */ 453 public function group( $file ) { 454 $gid = @filegroup( $this->sftp_path( $file ) ); 455 456 if ( ! $gid ) { 457 return false; 458 } 459 460 if ( ! function_exists( 'posix_getgrgid' ) ) { 461 return $gid; 462 } 463 464 $grouparray = posix_getgrgid( $gid ); 465 466 if ( ! $grouparray ) { 467 return false; 468 } 469 470 return $grouparray['name']; 471 } 472 473 /** 474 * Copies a file. 475 * 476 * @since 2.7.0 477 * 478 * @param string $source Path to the source file. 479 * @param string $destination Path to the destination file. 480 * @param bool $overwrite Optional. Whether to overwrite the destination file if it exists. 481 * Default false. 482 * @param int|false $mode Optional. The permissions as octal number, usually 0644 for files, 483 * 0755 for dirs. Default false. 484 * @return bool True on success, false on failure. 485 */ 486 public function copy( $source, $destination, $overwrite = false, $mode = false ) { 487 if ( ! $overwrite && $this->exists( $destination ) ) { 488 return false; 489 } 490 491 $content = $this->get_contents( $source ); 492 493 if ( false === $content ) { 494 return false; 495 } 496 497 return $this->put_contents( $destination, $content, $mode ); 498 } 499 500 /** 501 * Moves a file or directory. 502 * 503 * After moving files or directories, OPcache will need to be invalidated. 504 * 505 * If moving a directory fails, `copy_dir()` can be used for a recursive copy. 506 * 507 * Use `move_dir()` for moving directories with OPcache invalidation and a 508 * fallback to `copy_dir()`. 509 * 510 * @since 2.7.0 511 * 512 * @param string $source Path to the source file or directory. 513 * @param string $destination Path to the destination file or directory. 514 * @param bool $overwrite Optional. Whether to overwrite the destination if it exists. 515 * Default false. 516 * @return bool True on success, false on failure. 517 */ 518 public function move( $source, $destination, $overwrite = false ) { 519 if ( $this->exists( $destination ) ) { 520 if ( $overwrite ) { 521 // We need to remove the destination before we can rename the source. 522 $this->delete( $destination, false, 'f' ); 523 } else { 524 // If we're not overwriting, the rename will fail, so return early. 525 return false; 526 } 527 } 528 529 return ssh2_sftp_rename( $this->sftp_link, $source, $destination ); 530 } 531 532 /** 533 * Deletes a file or directory. 534 * 535 * @since 2.7.0 536 * 537 * @param string $file Path to the file or directory. 538 * @param bool $recursive Optional. If set to true, deletes files and folders recursively. 539 * Default false. 540 * @param string|false $type Type of resource. 'f' for file, 'd' for directory. 541 * Default false. 542 * @return bool True on success, false on failure. 543 */ 544 public function delete( $file, $recursive = false, $type = false ) { 545 if ( 'f' === $type || $this->is_file( $file ) ) { 546 return ssh2_sftp_unlink( $this->sftp_link, $file ); 547 } 548 549 if ( ! $recursive ) { 550 return ssh2_sftp_rmdir( $this->sftp_link, $file ); 551 } 552 553 $filelist = $this->dirlist( $file ); 554 555 if ( is_array( $filelist ) ) { 556 foreach ( $filelist as $filename => $fileinfo ) { 557 $this->delete( $file . '/' . $filename, $recursive, $fileinfo['type'] ); 558 } 559 } 560 561 return ssh2_sftp_rmdir( $this->sftp_link, $file ); 562 } 563 564 /** 565 * Checks if a file or directory exists. 566 * 567 * @since 2.7.0 568 * 569 * @param string $path Path to file or directory. 570 * @return bool Whether $path exists or not. 571 */ 572 public function exists( $path ) { 573 return file_exists( $this->sftp_path( $path ) ); 574 } 575 576 /** 577 * Checks if resource is a file. 578 * 579 * @since 2.7.0 580 * 581 * @param string $file File path. 582 * @return bool Whether $file is a file. 583 */ 584 public function is_file( $file ) { 585 return is_file( $this->sftp_path( $file ) ); 586 } 587 588 /** 589 * Checks if resource is a directory. 590 * 591 * @since 2.7.0 592 * 593 * @param string $path Directory path. 594 * @return bool Whether $path is a directory. 595 */ 596 public function is_dir( $path ) { 597 return is_dir( $this->sftp_path( $path ) ); 598 } 599 600 /** 601 * Checks if a file is readable. 602 * 603 * @since 2.7.0 604 * 605 * @param string $file Path to file. 606 * @return bool Whether $file is readable. 607 */ 608 public function is_readable( $file ) { 609 return is_readable( $this->sftp_path( $file ) ); 610 } 611 612 /** 613 * Checks if a file or directory is writable. 614 * 615 * @since 2.7.0 616 * 617 * @param string $path Path to file or directory. 618 * @return bool Whether $path is writable. 619 */ 620 public function is_writable( $path ) { 621 // PHP will base its writable checks on system_user === file_owner, not ssh_user === file_owner. 622 return true; 623 } 624 625 /** 626 * Gets the file's last access time. 627 * 628 * @since 2.7.0 629 * 630 * @param string $file Path to file. 631 * @return int|false Unix timestamp representing last access time, false on failure. 632 */ 633 public function atime( $file ) { 634 return fileatime( $this->sftp_path( $file ) ); 635 } 636 637 /** 638 * Gets the file modification time. 639 * 640 * @since 2.7.0 641 * 642 * @param string $file Path to file. 643 * @return int|false Unix timestamp representing modification time, false on failure. 644 */ 645 public function mtime( $file ) { 646 return filemtime( $this->sftp_path( $file ) ); 647 } 648 649 /** 650 * Gets the file size (in bytes). 651 * 652 * @since 2.7.0 653 * 654 * @param string $file Path to file. 655 * @return int|false Size of the file in bytes on success, false on failure. 656 */ 657 public function size( $file ) { 658 return filesize( $this->sftp_path( $file ) ); 659 } 660 661 /** 662 * Sets the access and modification times of a file. 663 * 664 * Note: Not implemented. 665 * 666 * @since 2.7.0 667 * 668 * @param string $file Path to file. 669 * @param int $time Optional. Modified time to set for file. 670 * Default 0. 671 * @param int $atime Optional. Access time to set for file. 672 * Default 0. 673 */ 674 public function touch( $file, $time = 0, $atime = 0 ) { 675 // Not implemented. 676 } 677 678 /** 679 * Creates a directory. 680 * 681 * @since 2.7.0 682 * 683 * @param string $path Path for new directory. 684 * @param int|false $chmod Optional. The permissions as octal number (or false to skip chmod). 685 * Default false. 686 * @param string|int|false $chown Optional. A user name or number (or false to skip chown). 687 * Default false. 688 * @param string|int|false $chgrp Optional. A group name or number (or false to skip chgrp). 689 * Default false. 690 * @return bool True on success, false on failure. 691 */ 692 public function mkdir( $path, $chmod = false, $chown = false, $chgrp = false ) { 693 $path = untrailingslashit( $path ); 694 695 if ( empty( $path ) ) { 696 return false; 697 } 698 699 if ( ! $chmod ) { 700 $chmod = FS_CHMOD_DIR; 701 } 702 703 if ( ! ssh2_sftp_mkdir( $this->sftp_link, $path, $chmod, true ) ) { 704 return false; 705 } 706 707 // Set directory permissions. 708 ssh2_sftp_chmod( $this->sftp_link, $path, $chmod ); 709 710 if ( $chown ) { 711 $this->chown( $path, $chown ); 712 } 713 714 if ( $chgrp ) { 715 $this->chgrp( $path, $chgrp ); 716 } 717 718 return true; 719 } 720 721 /** 722 * Deletes a directory. 723 * 724 * @since 2.7.0 725 * 726 * @param string $path Path to directory. 727 * @param bool $recursive Optional. Whether to recursively remove files/directories. 728 * Default false. 729 * @return bool True on success, false on failure. 730 */ 731 public function rmdir( $path, $recursive = false ) { 732 return $this->delete( $path, $recursive ); 733 } 734 735 /** 736 * Gets details for files in a directory or a specific file. 737 * 738 * @since 2.7.0 739 * 740 * @param string $path Path to directory or file. 741 * @param bool $include_hidden Optional. Whether to include details of hidden ("." prefixed) files. 742 * Default true. 743 * @param bool $recursive Optional. Whether to recursively include file details in nested directories. 744 * Default false. 745 * @return array|false { 746 * Array of arrays containing file information. False if unable to list directory contents. 747 * 748 * @type array ...$0 { 749 * Array of file information. Note that some elements may not be available on all filesystems. 750 * 751 * @type string $name Name of the file or directory. 752 * @type string $perms *nix representation of permissions. 753 * @type string $permsn Octal representation of permissions. 754 * @type false $number File number. Always false in this context. 755 * @type string|false $owner Owner name or ID, or false if not available. 756 * @type string|false $group File permissions group, or false if not available. 757 * @type int|string|false $size Size of file in bytes. May be a numeric string. 758 * False if not available. 759 * @type int|string|false $lastmodunix Last modified unix timestamp. May be a numeric string. 760 * False if not available. 761 * @type string|false $lastmod Last modified month (3 letters) and day (without leading 0), or 762 * false if not available. 763 * @type string|false $time Last modified time, or false if not available. 764 * @type string $type Type of resource. 'f' for file, 'd' for directory, 'l' for link. 765 * @type array|false $files If a directory and `$recursive` is true, contains another array of 766 * files. False if unable to list directory contents. 767 * } 768 * } 769 */ 770 public function dirlist( $path, $include_hidden = true, $recursive = false ) { 771 if ( $this->is_file( $path ) ) { 772 $limit_file = basename( $path ); 773 $path = dirname( $path ); 774 } else { 775 $limit_file = false; 776 } 777 778 if ( ! $this->is_dir( $path ) || ! $this->is_readable( $path ) ) { 779 return false; 780 } 781 782 $ret = array(); 783 $dir = dir( $this->sftp_path( $path ) ); 784 785 if ( ! $dir ) { 786 return false; 787 } 788 789 $path = trailingslashit( $path ); 790 791 while ( false !== ( $entry = $dir->read() ) ) { 792 $struc = array(); 793 $struc['name'] = $entry; 794 795 if ( '.' === $struc['name'] || '..' === $struc['name'] ) { 796 continue; // Do not care about these folders. 797 } 798 799 if ( ! $include_hidden && '.' === $struc['name'][0] ) { 800 continue; 801 } 802 803 if ( $limit_file && $struc['name'] !== $limit_file ) { 804 continue; 805 } 806 807 $struc['perms'] = $this->gethchmod( $path . $entry ); 808 $struc['permsn'] = $this->getnumchmodfromh( $struc['perms'] ); 809 $struc['number'] = false; 810 $struc['owner'] = $this->owner( $path . $entry ); 811 $struc['group'] = $this->group( $path . $entry ); 812 $struc['size'] = $this->size( $path . $entry ); 813 $struc['lastmodunix'] = $this->mtime( $path . $entry ); 814 $struc['lastmod'] = gmdate( 'M j', $struc['lastmodunix'] ); 815 $struc['time'] = gmdate( 'h:i:s', $struc['lastmodunix'] ); 816 $struc['type'] = $this->is_dir( $path . $entry ) ? 'd' : 'f'; 817 818 if ( 'd' === $struc['type'] ) { 819 if ( $recursive ) { 820 $struc['files'] = $this->dirlist( $path . $struc['name'], $include_hidden, $recursive ); 821 } else { 822 $struc['files'] = array(); 823 } 824 } 825 826 $ret[ $struc['name'] ] = $struc; 827 } 828 829 $dir->close(); 830 unset( $dir ); 831 832 return $ret; 833 } 834 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Sat Dec 21 08:20:01 2024 | Cross-referenced by PHPXref |