thephpleague /
flysystem-sftp
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | |||
| 3 | namespace League\Flysystem\Sftp; |
||
| 4 | |||
| 5 | use InvalidArgumentException; |
||
| 6 | use League\Flysystem\Adapter\AbstractFtpAdapter; |
||
| 7 | use League\Flysystem\Adapter\Polyfill\StreamedCopyTrait; |
||
| 8 | use League\Flysystem\AdapterInterface; |
||
| 9 | use League\Flysystem\Config; |
||
| 10 | use League\Flysystem\Util; |
||
| 11 | use phpseclib\Crypt\RSA; |
||
| 12 | use phpseclib\Net\SFTP; |
||
| 13 | use phpseclib\System\SSH\Agent; |
||
| 14 | |||
| 15 | class SftpAdapter extends AbstractFtpAdapter |
||
| 16 | { |
||
| 17 | use StreamedCopyTrait; |
||
| 18 | |||
| 19 | /** |
||
| 20 | * @var SFTP |
||
| 21 | */ |
||
| 22 | protected $connection; |
||
| 23 | |||
| 24 | /** |
||
| 25 | * @var int |
||
| 26 | */ |
||
| 27 | protected $port = 22; |
||
| 28 | |||
| 29 | /** |
||
| 30 | * @var string |
||
| 31 | */ |
||
| 32 | protected $hostFingerprint; |
||
| 33 | |||
| 34 | /** |
||
| 35 | * @var string |
||
| 36 | */ |
||
| 37 | protected $privateKey; |
||
| 38 | |||
| 39 | /** |
||
| 40 | * @var bool |
||
| 41 | */ |
||
| 42 | protected $useAgent = false; |
||
| 43 | |||
| 44 | /** |
||
| 45 | * @var Agent |
||
| 46 | */ |
||
| 47 | private $agent; |
||
| 48 | |||
| 49 | /** |
||
| 50 | * @var array |
||
| 51 | */ |
||
| 52 | protected $configurable = ['host', 'hostFingerprint', 'port', 'username', 'password', 'useAgent', 'agent', 'timeout', 'root', 'privateKey', 'passphrase', 'permPrivate', 'permPublic', 'directoryPerm', 'NetSftpConnection']; |
||
| 53 | |||
| 54 | /** |
||
| 55 | * @var array |
||
| 56 | */ |
||
| 57 | protected $statMap = ['mtime' => 'timestamp', 'size' => 'size']; |
||
| 58 | |||
| 59 | /** |
||
| 60 | * @var int |
||
| 61 | */ |
||
| 62 | protected $directoryPerm = 0744; |
||
| 63 | |||
| 64 | /** |
||
| 65 | * @var string |
||
| 66 | */ |
||
| 67 | private $passphrase; |
||
| 68 | |||
| 69 | /** |
||
| 70 | * Prefix a path. |
||
| 71 | * |
||
| 72 | * @param string $path |
||
| 73 | * |
||
| 74 | * @return string |
||
| 75 | */ |
||
| 76 | 9 | protected function prefix($path) |
|
| 77 | { |
||
| 78 | 9 | return $this->root.ltrim($path, $this->separator); |
|
| 79 | } |
||
| 80 | |||
| 81 | /** |
||
| 82 | * Set the finger print of the public key of the host you are connecting to. |
||
| 83 | * |
||
| 84 | * If the key does not match the server identification, the connection will |
||
| 85 | * be aborted. |
||
| 86 | * |
||
| 87 | * @param string $fingerprint Example: '88:76:75:96:c1:26:7c:dd:9f:87:50:db:ac:c4:a8:7c'. |
||
| 88 | * |
||
| 89 | * @return $this |
||
| 90 | */ |
||
| 91 | 9 | public function setHostFingerprint($fingerprint) |
|
| 92 | { |
||
| 93 | 9 | $this->hostFingerprint = $fingerprint; |
|
| 94 | |||
| 95 | 9 | return $this; |
|
| 96 | } |
||
| 97 | |||
| 98 | /** |
||
| 99 | * Set the private key (string or path to local file). |
||
| 100 | * |
||
| 101 | * @param string $key |
||
| 102 | * |
||
| 103 | * @return $this |
||
| 104 | */ |
||
| 105 | 12 | public function setPrivateKey($key) |
|
| 106 | { |
||
| 107 | 12 | $this->privateKey = $key; |
|
| 108 | |||
| 109 | 12 | return $this; |
|
| 110 | } |
||
| 111 | |||
| 112 | /** |
||
| 113 | * Set the passphrase for the privatekey. |
||
| 114 | * |
||
| 115 | * @param string $passphrase |
||
| 116 | * |
||
| 117 | * @return $this |
||
| 118 | */ |
||
| 119 | 6 | public function setPassphrase($passphrase) |
|
| 120 | { |
||
| 121 | 6 | $this->passphrase = $passphrase; |
|
| 122 | |||
| 123 | 6 | return $this; |
|
| 124 | } |
||
| 125 | |||
| 126 | /** |
||
| 127 | * @param boolean $useAgent |
||
| 128 | * |
||
| 129 | * @return $this |
||
| 130 | */ |
||
| 131 | public function setUseAgent($useAgent) |
||
| 132 | { |
||
| 133 | $this->useAgent = (bool) $useAgent; |
||
| 134 | |||
| 135 | return $this; |
||
| 136 | } |
||
| 137 | |||
| 138 | /** |
||
| 139 | * @param Agent $agent |
||
| 140 | * |
||
| 141 | * @return $this |
||
| 142 | */ |
||
| 143 | public function setAgent(Agent $agent) |
||
| 144 | { |
||
| 145 | $this->agent = $agent; |
||
| 146 | |||
| 147 | return $this; |
||
| 148 | } |
||
| 149 | |||
| 150 | /** |
||
| 151 | * Set permissions for new directory |
||
| 152 | * |
||
| 153 | * @param int $directoryPerm |
||
| 154 | * |
||
| 155 | * @return $this |
||
| 156 | */ |
||
| 157 | 6 | public function setDirectoryPerm($directoryPerm) |
|
| 158 | { |
||
| 159 | 6 | $this->directoryPerm = $directoryPerm; |
|
| 160 | |||
| 161 | 6 | return $this; |
|
| 162 | } |
||
| 163 | |||
| 164 | /** |
||
| 165 | * Get permissions for new directory |
||
| 166 | * |
||
| 167 | * @return int |
||
| 168 | */ |
||
| 169 | 3 | public function getDirectoryPerm() |
|
| 170 | { |
||
| 171 | 3 | return $this->directoryPerm; |
|
| 172 | } |
||
| 173 | |||
| 174 | /** |
||
| 175 | * Inject the SFTP instance. |
||
| 176 | * |
||
| 177 | * @param SFTP $connection |
||
| 178 | * |
||
| 179 | * @return $this |
||
| 180 | */ |
||
| 181 | 36 | public function setNetSftpConnection(SFTP $connection) |
|
| 182 | { |
||
| 183 | 36 | $this->connection = $connection; |
|
| 184 | |||
| 185 | 36 | return $this; |
|
| 186 | } |
||
| 187 | |||
| 188 | /** |
||
| 189 | * Connect. |
||
| 190 | */ |
||
| 191 | 27 | public function connect() |
|
| 192 | { |
||
| 193 | 27 | $this->connection = $this->connection ?: new SFTP($this->host, $this->port, $this->timeout); |
|
| 194 | 27 | $this->connection->disableStatCache(); |
|
| 195 | 27 | $this->login(); |
|
| 196 | 18 | $this->setConnectionRoot(); |
|
| 197 | 15 | } |
|
| 198 | |||
| 199 | /** |
||
| 200 | * Login. |
||
| 201 | * |
||
| 202 | * @throws ConnectionErrorException |
||
| 203 | */ |
||
| 204 | 27 | protected function login() |
|
| 205 | { |
||
| 206 | 27 | if ($this->hostFingerprint) { |
|
| 207 | 9 | $publicKey = $this->connection->getServerPublicHostKey(); |
|
| 208 | |||
| 209 | 9 | if ($publicKey === false) { |
|
| 210 | 3 | throw new ConnectionErrorException('Could not connect to server to verify public key.'); |
|
| 211 | } |
||
| 212 | |||
| 213 | 6 | $actualFingerprint = $this->getHexFingerprintFromSshPublicKey($publicKey); |
|
| 214 | |||
| 215 | 6 | if (0 !== strcasecmp($this->hostFingerprint, $actualFingerprint)) { |
|
| 216 | 3 | throw new ConnectionErrorException('The authenticity of host '.$this->host.' can\'t be established.'); |
|
| 217 | } |
||
| 218 | } |
||
| 219 | |||
| 220 | 21 | $authentication = $this->getAuthentication(); |
|
| 221 | |||
| 222 | |||
| 223 | 21 | if ($this->connection->login($this->getUsername(), $authentication)) { |
|
| 224 | 15 | goto past_login; |
|
| 225 | } |
||
| 226 | |||
| 227 | // try double authentication, key is already given so now give password |
||
| 228 | 6 | if ($authentication instanceof RSA && $this->connection->login($this->getUsername(), $this->getPassword())) { |
|
| 229 | 3 | goto past_login; |
|
| 230 | } |
||
| 231 | |||
| 232 | 3 | throw new ConnectionErrorException('Could not login with username: '.$this->getUsername().', host: '.$this->host); |
|
| 233 | |||
| 234 | past_login: |
||
| 235 | |||
| 236 | 18 | if ($authentication instanceof Agent) { |
|
| 237 | $authentication->startSSHForwarding($this->connection); |
||
| 238 | } |
||
| 239 | 18 | } |
|
| 240 | |||
| 241 | /** |
||
| 242 | * Convert the SSH RSA public key into a hex formatted fingerprint. |
||
| 243 | * |
||
| 244 | * @param string $publickey |
||
| 245 | * @return string Hex formatted fingerprint, e.g. '88:76:75:96:c1:26:7c:dd:9f:87:50:db:ac:c4:a8:7c'. |
||
| 246 | */ |
||
| 247 | 6 | private function getHexFingerprintFromSshPublicKey ($publickey) |
|
| 248 | { |
||
| 249 | 6 | $content = explode(' ', $publickey, 3); |
|
| 250 | 6 | return implode(':', str_split(md5(base64_decode($content[1])), 2)); |
|
| 251 | } |
||
| 252 | |||
| 253 | /** |
||
| 254 | * Set the connection root. |
||
| 255 | * |
||
| 256 | * @throws InvalidRootException |
||
| 257 | */ |
||
| 258 | 18 | protected function setConnectionRoot() |
|
| 259 | { |
||
| 260 | 18 | $root = $this->getRoot(); |
|
| 261 | |||
| 262 | 18 | if (! $root) { |
|
|
0 ignored issues
–
show
|
|||
| 263 | 12 | return; |
|
| 264 | } |
||
| 265 | |||
| 266 | 6 | if (! $this->connection->chdir($root)) { |
|
| 267 | 3 | throw new InvalidRootException('Root is invalid or does not exist: '.$root); |
|
| 268 | } |
||
| 269 | |||
| 270 | 3 | $this->setRoot($this->connection->pwd()); |
|
| 271 | 3 | } |
|
| 272 | |||
| 273 | /** |
||
| 274 | * Get the password, either the private key or a plain text password. |
||
| 275 | * |
||
| 276 | * @return Agent|RSA|string |
||
| 277 | */ |
||
| 278 | 24 | public function getAuthentication() |
|
| 279 | { |
||
| 280 | 24 | if ($this->useAgent) { |
|
| 281 | return $this->getAgent(); |
||
| 282 | } |
||
| 283 | |||
| 284 | 24 | if ($this->privateKey) { |
|
| 285 | 6 | return $this->getPrivateKey(); |
|
| 286 | } |
||
| 287 | |||
| 288 | 18 | return $this->getPassword(); |
|
| 289 | } |
||
| 290 | |||
| 291 | /** |
||
| 292 | * Get the private key with the password or private key contents. |
||
| 293 | * |
||
| 294 | * @return RSA |
||
| 295 | */ |
||
| 296 | 12 | public function getPrivateKey() |
|
| 297 | { |
||
| 298 | 12 | if ("---" !== substr($this->privateKey, 0, 3) && is_file($this->privateKey)) { |
|
| 299 | 3 | $this->privateKey = file_get_contents($this->privateKey); |
|
| 300 | } |
||
| 301 | |||
| 302 | 12 | $key = new RSA(); |
|
| 303 | |||
| 304 | 12 | if ($password = $this->getPassphrase()) { |
|
| 305 | 12 | $key->setPassword($password); |
|
| 306 | } |
||
| 307 | |||
| 308 | 12 | $key->loadKey($this->privateKey); |
|
| 309 | |||
| 310 | 12 | return $key; |
|
| 311 | } |
||
| 312 | |||
| 313 | /** |
||
| 314 | * @return string |
||
| 315 | */ |
||
| 316 | 21 | public function getPassphrase() |
|
| 317 | { |
||
| 318 | 21 | if ($this->passphrase === null) { |
|
| 319 | //Added for backward compatibility |
||
| 320 | 15 | return $this->getPassword(); |
|
| 321 | } |
||
| 322 | 6 | return $this->passphrase; |
|
| 323 | } |
||
| 324 | |||
| 325 | /** |
||
| 326 | * @return Agent|bool |
||
| 327 | */ |
||
| 328 | public function getAgent() |
||
| 329 | { |
||
| 330 | if ( ! $this->agent instanceof Agent) { |
||
| 331 | $this->agent = new Agent(); |
||
| 332 | } |
||
| 333 | |||
| 334 | return $this->agent; |
||
| 335 | } |
||
| 336 | |||
| 337 | /** |
||
| 338 | * List the contents of a directory. |
||
| 339 | * |
||
| 340 | * @param string $directory |
||
| 341 | * @param bool $recursive |
||
| 342 | * |
||
| 343 | * @return array |
||
| 344 | */ |
||
| 345 | 9 | protected function listDirectoryContents($directory, $recursive = true) |
|
| 346 | { |
||
| 347 | 9 | $result = []; |
|
| 348 | 9 | $connection = $this->getConnection(); |
|
| 349 | 9 | $location = $this->prefix($directory); |
|
| 350 | 9 | $listing = $connection->rawlist($location); |
|
| 351 | |||
| 352 | 9 | if ($listing === false) { |
|
| 353 | 3 | return []; |
|
| 354 | } |
||
| 355 | |||
| 356 | 9 | foreach ($listing as $filename => $object) { |
|
| 357 | // When directory entries have a numeric filename they are changed to int |
||
| 358 | 9 | $filename = (string) $filename; |
|
| 359 | 9 | if (in_array($filename, ['.', '..'])) { |
|
| 360 | 3 | continue; |
|
| 361 | } |
||
| 362 | |||
| 363 | 9 | $path = empty($directory) ? $filename : ($directory.'/'.$filename); |
|
| 364 | 9 | $result[] = $this->normalizeListingObject($path, $object); |
|
| 365 | |||
| 366 | 9 | if ($recursive && isset($object['type']) && $object['type'] === NET_SFTP_TYPE_DIRECTORY) { |
|
| 367 | 5 | $result = array_merge($result, $this->listDirectoryContents($path)); |
|
| 368 | } |
||
| 369 | } |
||
| 370 | |||
| 371 | 9 | return $result; |
|
| 372 | } |
||
| 373 | |||
| 374 | /** |
||
| 375 | * Normalize a listing response. |
||
| 376 | * |
||
| 377 | * @param string $path |
||
| 378 | * @param array $object |
||
| 379 | * |
||
| 380 | * @return array |
||
| 381 | */ |
||
| 382 | 9 | protected function normalizeListingObject($path, array $object) |
|
| 383 | { |
||
| 384 | 9 | $permissions = $this->normalizePermissions($object['permissions']); |
|
| 385 | 9 | $type = isset($object['type']) && ($object['type'] === 2) ? 'dir' : 'file'; |
|
| 386 | |||
| 387 | 9 | $timestamp = $object['mtime']; |
|
| 388 | |||
| 389 | 9 | if ($type === 'dir') { |
|
| 390 | 9 | return compact('path', 'timestamp', 'type'); |
|
| 391 | } |
||
| 392 | |||
| 393 | 6 | $visibility = $permissions & 0044 ? AdapterInterface::VISIBILITY_PUBLIC : AdapterInterface::VISIBILITY_PRIVATE; |
|
| 394 | 6 | $size = (int) $object['size']; |
|
| 395 | |||
| 396 | 6 | return compact('path', 'timestamp', 'type', 'visibility', 'size'); |
|
| 397 | } |
||
| 398 | |||
| 399 | /** |
||
| 400 | * Disconnect. |
||
| 401 | */ |
||
| 402 | 18 | public function disconnect() |
|
| 403 | { |
||
| 404 | 18 | $this->connection = null; |
|
| 405 | 18 | } |
|
| 406 | |||
| 407 | /** |
||
| 408 | * @inheritdoc |
||
| 409 | */ |
||
| 410 | 6 | public function write($path, $contents, Config $config) |
|
| 411 | { |
||
| 412 | 6 | if ($this->upload($path, $contents, $config) === false) { |
|
| 413 | 6 | return false; |
|
| 414 | } |
||
| 415 | |||
| 416 | 6 | return compact('contents', 'path'); |
|
| 417 | } |
||
| 418 | |||
| 419 | /** |
||
| 420 | * @inheritdoc |
||
| 421 | */ |
||
| 422 | 6 | public function writeStream($path, $resource, Config $config) |
|
| 423 | { |
||
| 424 | 6 | if ($this->upload($path, $resource, $config) === false) { |
|
| 425 | 6 | return false; |
|
| 426 | } |
||
| 427 | |||
| 428 | 6 | return compact('path'); |
|
| 429 | } |
||
| 430 | |||
| 431 | /** |
||
| 432 | * Upload a file. |
||
| 433 | * |
||
| 434 | * @param string $path |
||
| 435 | * @param string|resource $contents |
||
| 436 | * @param Config $config |
||
| 437 | * @return bool |
||
| 438 | */ |
||
| 439 | 12 | public function upload($path, $contents, Config $config) |
|
| 440 | { |
||
| 441 | 12 | $connection = $this->getConnection(); |
|
| 442 | 12 | $this->ensureDirectory(Util::dirname($path)); |
|
| 443 | 12 | $config = Util::ensureConfig($config); |
|
| 444 | |||
| 445 | 12 | if (! $connection->put($path, $contents, SFTP::SOURCE_STRING)) { |
|
| 446 | 12 | return false; |
|
| 447 | } |
||
| 448 | |||
| 449 | 12 | if ($config && $visibility = $config->get('visibility')) { |
|
| 450 | 6 | $this->setVisibility($path, $visibility); |
|
| 451 | } |
||
| 452 | |||
| 453 | 12 | return true; |
|
| 454 | } |
||
| 455 | |||
| 456 | /** |
||
| 457 | * @inheritdoc |
||
| 458 | */ |
||
| 459 | 6 | public function read($path) |
|
| 460 | { |
||
| 461 | 6 | $connection = $this->getConnection(); |
|
| 462 | |||
| 463 | 6 | if (($contents = $connection->get($path)) === false) { |
|
| 464 | 6 | return false; |
|
| 465 | } |
||
| 466 | |||
| 467 | 6 | return compact('contents', 'path'); |
|
| 468 | } |
||
| 469 | |||
| 470 | /** |
||
| 471 | * @inheritdoc |
||
| 472 | */ |
||
| 473 | 3 | public function readStream($path) |
|
| 474 | { |
||
| 475 | 3 | $stream = tmpfile(); |
|
| 476 | 3 | $connection = $this->getConnection(); |
|
| 477 | |||
| 478 | 3 | if ($connection->get($path, $stream) === false) { |
|
| 479 | 3 | fclose($stream); |
|
| 480 | 3 | return false; |
|
| 481 | } |
||
| 482 | |||
| 483 | 3 | rewind($stream); |
|
| 484 | |||
| 485 | 3 | return compact('stream', 'path'); |
|
| 486 | } |
||
| 487 | |||
| 488 | /** |
||
| 489 | * @inheritdoc |
||
| 490 | */ |
||
| 491 | 3 | public function update($path, $contents, Config $config) |
|
| 492 | { |
||
| 493 | 3 | return $this->write($path, $contents, $config); |
|
| 494 | } |
||
| 495 | |||
| 496 | /** |
||
| 497 | * @inheritdoc |
||
| 498 | */ |
||
| 499 | 3 | public function updateStream($path, $contents, Config $config) |
|
| 500 | { |
||
| 501 | 3 | return $this->writeStream($path, $contents, $config); |
|
| 502 | } |
||
| 503 | |||
| 504 | /** |
||
| 505 | * @inheritdoc |
||
| 506 | */ |
||
| 507 | 3 | public function delete($path) |
|
| 508 | { |
||
| 509 | 3 | $connection = $this->getConnection(); |
|
| 510 | |||
| 511 | 3 | return $connection->delete($path); |
|
| 512 | } |
||
| 513 | |||
| 514 | /** |
||
| 515 | * @inheritdoc |
||
| 516 | */ |
||
| 517 | 3 | public function rename($path, $newpath) |
|
| 518 | { |
||
| 519 | 3 | $connection = $this->getConnection(); |
|
| 520 | |||
| 521 | 3 | return $connection->rename($path, $newpath); |
|
| 522 | } |
||
| 523 | |||
| 524 | /** |
||
| 525 | * @inheritdoc |
||
| 526 | */ |
||
| 527 | 3 | public function deleteDir($dirname) |
|
| 528 | { |
||
| 529 | 3 | $connection = $this->getConnection(); |
|
| 530 | |||
| 531 | 3 | return $connection->delete($dirname, true); |
|
| 532 | } |
||
| 533 | |||
| 534 | /** |
||
| 535 | * @inheritdoc |
||
| 536 | */ |
||
| 537 | 48 | public function has($path) |
|
| 538 | { |
||
| 539 | 48 | return $this->getMetadata($path); |
|
| 540 | } |
||
| 541 | |||
| 542 | /** |
||
| 543 | * @inheritdoc |
||
| 544 | */ |
||
| 545 | 54 | public function getMetadata($path) |
|
| 546 | { |
||
| 547 | 54 | $connection = $this->getConnection(); |
|
| 548 | 54 | $info = $connection->stat($path); |
|
| 549 | |||
| 550 | 54 | if ($info === false) { |
|
| 551 | 12 | return false; |
|
| 552 | } |
||
| 553 | |||
| 554 | 45 | $result = Util::map($info, $this->statMap); |
|
| 555 | 45 | $result['type'] = $info['type'] === NET_SFTP_TYPE_DIRECTORY ? 'dir' : 'file'; |
|
| 556 | 45 | $result['visibility'] = $info['permissions'] & $this->permPublic ? 'public' : 'private'; |
|
| 557 | |||
| 558 | 45 | return $result; |
|
| 559 | } |
||
| 560 | |||
| 561 | /** |
||
| 562 | * @inheritdoc |
||
| 563 | */ |
||
| 564 | 6 | public function getTimestamp($path) |
|
| 565 | { |
||
| 566 | 6 | return $this->getMetadata($path); |
|
| 567 | } |
||
| 568 | |||
| 569 | /** |
||
| 570 | * @inheritdoc |
||
| 571 | */ |
||
| 572 | 3 | public function getMimetype($path) |
|
| 573 | { |
||
| 574 | 3 | if (! $data = $this->read($path)) { |
|
| 575 | 3 | return false; |
|
| 576 | } |
||
| 577 | |||
| 578 | 3 | $data['mimetype'] = Util::guessMimeType($path, $data['contents']); |
|
| 579 | |||
| 580 | 3 | return $data; |
|
| 581 | } |
||
| 582 | |||
| 583 | /** |
||
| 584 | * @inheritdoc |
||
| 585 | */ |
||
| 586 | 3 | public function createDir($dirname, Config $config) |
|
| 587 | { |
||
| 588 | 3 | $connection = $this->getConnection(); |
|
| 589 | |||
| 590 | 3 | if (! $connection->mkdir($dirname, $this->directoryPerm, true)) { |
|
| 591 | 3 | return false; |
|
| 592 | } |
||
| 593 | |||
| 594 | 3 | return ['path' => $dirname]; |
|
| 595 | } |
||
| 596 | |||
| 597 | /** |
||
| 598 | * @inheritdoc |
||
| 599 | */ |
||
| 600 | 6 | public function getVisibility($path) |
|
| 601 | { |
||
| 602 | 6 | return $this->getMetadata($path); |
|
| 603 | } |
||
| 604 | |||
| 605 | /** |
||
| 606 | * @inheritdoc |
||
| 607 | */ |
||
| 608 | 12 | public function setVisibility($path, $visibility) |
|
| 609 | { |
||
| 610 | 12 | $visibility = ucfirst($visibility); |
|
| 611 | |||
| 612 | 12 | if (! isset($this->{'perm'.$visibility})) { |
|
| 613 | 3 | throw new InvalidArgumentException('Unknown visibility: '.$visibility); |
|
| 614 | } |
||
| 615 | |||
| 616 | 9 | $connection = $this->getConnection(); |
|
| 617 | |||
| 618 | 9 | return $connection->chmod($this->{'perm'.$visibility}, $path); |
|
| 619 | } |
||
| 620 | |||
| 621 | /** |
||
| 622 | * @inheritdoc |
||
| 623 | */ |
||
| 624 | 81 | public function isConnected() |
|
| 625 | { |
||
| 626 | 81 | if ($this->connection instanceof SFTP && $this->connection->isConnected()) { |
|
| 627 | 78 | return true; |
|
| 628 | } |
||
| 629 | |||
| 630 | 3 | return false; |
|
| 631 | } |
||
| 632 | } |
||
| 633 |
In PHP, under loose comparison (like
==, or!=, orswitchconditions), values of different types might be equal.For
stringvalues, the empty string''is a special case, in particular the following results might be unexpected: