Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGIFX] Use exceptions for connection root error resolving #1460

Merged
merged 1 commit into from
May 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions mocked-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ function ftp_pasv(...$arguments)
return return_mocked_value('ftp_pasv');
}

function ftp_pwd(...$arguments)
{
if ( ! is_mocked('ftp_pwd')) {
return \ftp_pwd(...$arguments);
}

return return_mocked_value('ftp_pwd');
}

function ftp_fput(...$arguments)
{
if ( ! is_mocked('ftp_fput')) {
Expand Down
18 changes: 14 additions & 4 deletions src/Ftp/FtpAdapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
use League\MimeTypeDetection\MimeTypeDetector;
use Throwable;

use function error_clear_last;
use function error_get_last;
use function ftp_chdir;
use function ftp_pwd;
use function is_string;

class FtpAdapter implements FilesystemAdapter
{
Expand Down Expand Up @@ -644,12 +646,20 @@ public function directoryExists(string $path): bool
private function resolveConnectionRoot($connection): string
{
$root = $this->connectionOptions->root();
error_clear_last();

if ($root !== '') {
ftp_chdir($connection, $root);
if ($root !== '' && @ftp_chdir($connection, $root) !== true) {
throw UnableToResolveConnectionRoot::itDoesNotExist($root, error_get_last()['message'] ?? '');
}

return ftp_pwd($connection);
error_clear_last();
$pwd = @ftp_pwd($connection);

if ( ! is_string($pwd)) {
throw UnableToResolveConnectionRoot::couldNotGetCurrentDirectory(error_get_last()['message'] ?? '');
}

return $pwd;
}

/**
Expand Down
50 changes: 50 additions & 0 deletions src/Ftp/FtpAdapterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

use League\Flysystem\FilesystemAdapter;

use function mock_function;
use function reset_function_mocks;

/**
* @group ftp
*/
Expand Down Expand Up @@ -46,4 +49,51 @@ public function disconnect_after_destruct(): void
static::clearFilesystemAdapterCache();
$this->assertFalse((new NoopCommandConnectivityChecker())->isConnected($connection));
}

/**
* @test
*/
public function not_being_able_to_resolve_connection_root(): void
{
$options = FtpConnectionOptions::fromArray([
'host' => 'localhost',
'port' => 2121,
'timestampsOnUnixListingsEnabled' => true,
'root' => '/invalid/root',
'username' => 'foo',
'password' => 'pass',
]);

$adapter = new FtpAdapter($options);

$this->expectExceptionObject(UnableToResolveConnectionRoot::itDoesNotExist('/invalid/root'));

$adapter->delete('something');
}

/**
* @test
*/
public function not_being_able_to_resolve_connection_root_pwd(): void
{
$options = FtpConnectionOptions::fromArray([
'host' => 'localhost',
'port' => 2121,
'timestampsOnUnixListingsEnabled' => true,
'root' => '/home/foo/upload/',
'username' => 'foo',
'password' => 'pass',
]);

$this->expectExceptionObject(UnableToResolveConnectionRoot::couldNotGetCurrentDirectory());
mock_function('ftp_pwd', false);

$adapter = new FtpAdapter($options);
$adapter->delete('something');
}

protected function tearDown(): void
{
reset_function_mocks();
}
}
6 changes: 3 additions & 3 deletions src/Ftp/FtpConnectionProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ private function enableUtf8Mode(FtpConnectionOptions $options, $connection): voi
return;
}

$response = ftp_raw($connection, "OPTS UTF8 ON");
$response = @ftp_raw($connection, "OPTS UTF8 ON");

if ( ! in_array(substr($response[0], 0, 3), ['200', '202'])) {
throw new UnableToEnableUtf8Mode(
Expand All @@ -88,7 +88,7 @@ private function ignorePassiveAddress(FtpConnectionOptions $options, $connection
return;
}

if ( ! ftp_set_option($connection, FTP_USEPASVADDRESS, ! $ignorePassiveAddress)) {
if ( ! @ftp_set_option($connection, FTP_USEPASVADDRESS, ! $ignorePassiveAddress)) {
throw UnableToSetFtpOption::whileSettingOption('FTP_USEPASVADDRESS');
}
}
Expand All @@ -98,7 +98,7 @@ private function ignorePassiveAddress(FtpConnectionOptions $options, $connection
*/
private function makeConnectionPassive(FtpConnectionOptions $options, $connection): void
{
if ( ! ftp_pasv($connection, $options->passive())) {
if ( ! @ftp_pasv($connection, $options->passive())) {
throw new UnableToMakeConnectionPassive(
'Could not set passive mode for connection: ' . $options->host() . '::' . $options->port()
);
Expand Down
4 changes: 3 additions & 1 deletion src/Ftp/FtpConnectionProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use League\Flysystem\AdapterTestUtilities\RetryOnTestException;
use PHPUnit\Framework\TestCase;

use function ftp_close;

/**
* @group ftp
*/
Expand Down Expand Up @@ -54,7 +56,7 @@ public function connecting_successfully(): void
'root' => '/home/foo/upload',
'username' => 'foo',
'password' => 'pass',
]);
]);

$this->runScenario(function () use ($options) {
$connection = $this->connectionProvider->createConnection($options);
Expand Down
30 changes: 30 additions & 0 deletions src/Ftp/UnableToResolveConnectionRoot.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace League\Flysystem\Ftp;

use RuntimeException;
use Throwable;

final class UnableToResolveConnectionRoot extends RuntimeException implements FtpConnectionException
{
private function __construct(string $message, Throwable $previous = null)
{
parent::__construct($message, 0, $previous);
}

public static function itDoesNotExist(string $root): UnableToResolveConnectionRoot
{
return new UnableToResolveConnectionRoot(
'Unable to resolve connection root. It does not seem to exist: ' . $root
);
}

public static function couldNotGetCurrentDirectory(string $message = ''): UnableToResolveConnectionRoot
{
return new UnableToResolveConnectionRoot(
'Unable to resolve connection root. Could not resolve the current directory. ' . $message
);
}
}