API / Minn\Mail / SmtpSession

SmtpSession final class

Minn\Mail\SmtpSession · public/minn/src/Minn/Mail/SmtpSession.php · 461 lines

One SMTP conversation as the reference's client holds it: the socket, the last reply, the server's extensions from EHLO, and the last error as {error, detail, smtp_code, smtp_code_ex}. Every command names itself in its error ("RCPT TO command failed"); a command sent before connecting fails as "Called X without being connected". Debug text goes to the given closure with its level: 1 client lines, 2 server replies, 3 connection events, 4 raw inbound lines. Credentials never reach it.

Uses 1
Used by 2

Constants and properties

constMAX_LINE = 998
privateCREDENTIALS = array ( 0 => 'User & Password', 1 => 'Username', 2 => 'Password', )
privateAUTH_ORDER = array ( 0 => 'CRAM-MD5', 1 => 'LOGIN', 2 => 'PLAIN', 3 => 'XOAUTH2', )
privateTRANSACTION_IDS = array ( 0 => '/queued as ([^\\s]+)/i', 1 => '/\\bOK id=([^\\s]+)/i', 2 => '/^\\d{3} 2\\.0\\.0 ([^\\s]+) Message accepted for delivery/m', 3 => '/^\\d{3} 2\\.\\d\\.0 <?([^\\s@>]+)@[^\\s]* Queued mail for delivery/m', 4 => '/Message Queued \\(([^)]+)\\)/i', 5 => '/^\\d{3} Ok ([^\\s]+)/m', )
privatemixed $socket
privatearray $error
privatestring $heloReply
private?array $caps
privatestring $lastReply
privatestring|false|null $transactionId
privateint $timeout
privateint $timelimit
privatereadonly Closure $debug

Promoted from the constructor.

Public

new__construct(Closure $debug)#
$debugClosure(string, int): void
methodlimits(int $timeout, int $timelimit): void#

How long one read may wait and how long a whole reply may take, in seconds.

methodconnect(string $host, ?int $port, int $timeout, array $options): bool#

Opens the connection and reads the greeting.

$optionsarray<string, mixed>stream context options
methodstartTls(): bool#

Asks for TLS and turns it on.

methodauthenticate(string $user, string $password, string $type, ?string $oauth): bool#

Signs in with the named mechanism, or the strongest the server offers when it is empty or not offered.

$oauthstring|nulla ready XOAUTH2 token, base64, when there is one
methodconnected(): bool#

Whether the connection is open; a socket closed at the far end is closed here too.

methodclose(): void#

Closes the socket and forgets the server's greeting and extensions; the last error stays.

methoddata(string $message): bool#

Sends a message: DATA, the lines (a line past 998 characters split at its last space, or at 997 without one; header continuations indented; leading dots doubled), then the closing dot.

methodhello(string $host): bool#

EHLO, falling back to HELO; the extensions come from the EHLO reply.

methodmail(string $from, string $parameters): bool#

MAIL FROM, with the given parameters (" XVERP", " SMTPUTF8") after the address.

methodquit(string $onError = 'close'): bool#

QUIT; the connection closes when it succeeds, or anyway unless told to keep it.

methodrecipient(string $address, string $dsn): bool#

RCPT TO, with the delivery notifications asked for (NEVER, SUCCESS, FAILURE, DELAY).

methodxclient(array $vars, array $allowed): bool#

XCLIENT with the named attributes that are in $allowed; others are dropped.

$varsarray<string, string>
$allowedlist<string>
methodturn(): bool#

TURN is refused here, as the reference refuses it.

methodcommand(string $name, string $line, array $expect): bool#

Sends one command and reads the reply; a reply outside $expect fails with "{name} command failed" and the reply's detail and codes.

$expectlist<int>
methodsend(string $data, string $command): int|false#

Writes raw text to the server; credential commands show as hidden in the debug text.

methoderror(): array#

The last error; every field is empty after a command that succeeded.

@return array{error: string, detail: string, smtp_code: int|string, smtp_code_ex: string}
methodextensions(): ?array#

The extensions from the last EHLO (or the HELO greeting), null before one.

@return array<string, mixed>|null
methodextension(string $name): mixed#

One extension's value: true for a bare one, a string or list for one with arguments, false when absent, null before EHLO.

methodlastReply(): string#

The server's last reply, every line.

methodtransactionId(): string|false|null#

The queue id the server gave the last message: null before one was sent, false when none was recognised.

staticparseReply(string $reply): array#

The reply's code, enhanced code (when present) and the text after them.

@return array{0: int, 1: string, 2: string}

Internals 9

Private and protected methods: how the class does its work. Not a contract; they change whenever the code is tidied.

privategreet(string $verb, string $host): bool#
privateextensionsFrom(string $verb, string $reply): array#
@return array<string, mixed>
privatemechanism(string $type, ?string $oauth): string#
privateunsupported(string $type): bool#
privateopen(string $host, int $port, int $timeout, array $options)#
$optionsarray<string, mixed>
privatelines(): string#
privatefailed(int $no, string $text, string $file, int $line): bool#
privatesetError(string $message, string $detail = '', string|int $code = '', string $codeEx = ''): void#
privatesay(string $text, int $level): void#