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