php-imap
php-imap copied to clipboard
RuntimeException: failed to read - connection closed? but Client::isConnected() returns true
Describe the bug
vendor/webklex/php-imap/src/Connection/Protocols/ImapProtocol.php:109: RuntimeException: failed to read - connection closed? when deleting a folder, the folder is deleted successfully, but the exception is still thrown. Also, the $client->isConnected() method returns true.
The RuntimeException only occurs when dealing with folders.
Used config Default config.
Code to Reproduce There are 3 classes involved in managing the connections.
User.php- contains the imap credentialsEmailClientProvider.php- a singleton that stores an array of all the Clients (there is a connection for each user)EmailClient- handles the creation of the Client
Folder deletion
/** @var Client $mailbox */
$mailbox = $user->getMailboxAttribute();
$folderToDelete = $mailbox->getFolderByPath($request->folder->path);
$folderToDelete->delete(); // this line throws the error
User.php
class User
{
...
public function getMailboxAttribute(): Client
{
return EmailClientProvider::IMAPMailbox($this->emailCredentials);
}
}
EmailClientProvider.php
...
public static function IMAPMailbox(EmailCredentials $emailCredentials): Client
{
return self::getInstance()->getIMAPClient($emailCredentials);
}
protected function getIMAPClient(EmailCredentials $emailCredentials): Client
{
return $this->getEmailClient($emailCredentials)->IMAPMailbox();
}
// returns the email client and creates one if needed
protected function getEmailClient(EmailCredentials $emailCredentials): EmailClient
{
if (! array_key_exists($emailCredentials->id, $this->emailClients)) {
$this->emailClients[$emailCredentials->id] = new EmailClient($emailCredentials);
}
return $this->emailClients[$emailCredentials->id];
}
EmailClient.php
public function __construct(
protected EmailCredentials $credentials,
) {
...
$this->createAndCheckConnections();
}
protected function createAndCheckConnections(): void
{
$this->IMAPClient = $this->createIMAPClient();
$this->SMTPClient = $this->createSMTPClient();
}
protected function createIMAPClient(): IMAPClient
{
$clientManager = new IMAPClientManager();
$client = $clientManager->make([
'host' => $this->credentials->imap_host,
'port' => $this->credentials->imap_port,
'protocol' => $this->credentials->imap_protocol,
'encryption' => 'ssl',
'validate_cert' => true,
'username' => $this->credentials->imap_username,
'password' => $this->credentials->imap_password,
]);
$client->checkConnection(); // start the connection
return $client;
}
Expected behavior The expected behavior is that the folder gets deleted without an exception being thrown.
Desktop:
- OS: Arch Linux x86_64
- PHP: 8.1.3
- Version 3.2.0 (same error happened with v2.7.2)
- Provider: Roundcube Webmail (cPanel)
Additional context This is run within a Laravel app.
This is the test that throws the RuntimeException
// create a user with some valid IMAP credentials
$bob = createUserWithInbox();
actingAs($bob);
// fetch a random folder
$mailbox = $bob->mailbox;
$randomFolder = $mailbox->getFolders(false)
->filter(fn (Folder $folder) => str_contains($folder->path, 'testing'))
->random();
// trigger folder deletion action
$response = jsonApi()
->delete(route(
'v1.email-folders.destroy',
['email_folder' => $randomFolder->path]
));
assertNull($mailbox->getFolderByPath($randomFolder->path));
Hi @catabozan,
thanks for the detailed error description!
The exception is thrown in the ImapProtocol::nextLine() method. So there is a problem reading the response after the deletion happened.
Now the question is which response could not be evaluated. If the flag $expunge is true (it is true by default) of Folder::delete($expunge = true), then directly after the DELETE an EXPUNGE command is sent to the server.
Could you turn on the debug mode and provide us with the output?