What was broken
In SMTP, after the DATA command the server treats every following line as message content until it sees a line with a single dot. If sending broke off at exactly that point, say because of an exception or a timeout, and the application then closed the connection with quit(), PHPMailer sent a QUIT command. The server did not see the end of the session in it, only another line of the email, so it never replied, and the client waited for an answer until the time limit ran out. Issue #2994 described the problem.
What we changed
We followed the idea from the discussion in #3121: the SMTP class remembers the last command it sent, and quit() checks it before sending anything.
sendCommand() records the state on every command and close() clears it. We pointed out one limitation in the pull request: a DATA command the server rejected also leaves the state at DATA, and we offered to track only accepted commands if the maintainer preferred that.
How we tested it
A new test, testQuitDuringData, opens a connection, sends MAIL and RCPT, starts DATA without sending the message, as if the send had been interrupted, and calls quit(). It then checks the client debug output to confirm that QUIT was not sent and that the connection is closed.
The result
The change was merged on 4 October 2026. Applications that clean up the connection after a failed send no longer hang until the timeout or append QUIT to an abandoned message.