I needed a quick ‘n dirty script to test outgoing SMTP email via PHP and found php-smtp.

Pasting it here in case the repo ever disappears:

<?php
 
namespace Snipworks\Smtp;
 
/**
 * Send email class using SMTP Authentication
 *
 * @class Email
 * @package Snipworks\SMTP
 */
class Email
{
    const CRLF = "\r\n";
    const TLS = 'tcp';
    const SSL = 'ssl';
    const OK = 250;
 
    /** @var string $server */
    protected $server;
 
    /** @var string $hostname */
    protected $hostname;
 
    /** @var int $port */
    protected $port;
 
    /** @var resource $socket */
    protected $socket;
 
    /** @var string $username */
    protected $username;
 
    /** @var string $password */
    protected $password;
 
    /** @var int $connectionTimeout */
    protected $connectionTimeout;
 
    /** @var int $responseTimeout */
    protected $responseTimeout;
 
    /** @var string $subject */
    protected $subject;
 
    /** @var array $to */
    protected $to = array();
 
    /** @var array $cc */
    protected $cc = array();
 
    /** @var array $bcc */
    protected $bcc = array();
 
    /** @var array $from */
    protected $from = array();
 
    /** @var array $replyTo */
    protected $replyTo = array();
 
    /** @var array $attachments */
    protected $attachments = array();
 
    /** @var string|null $protocol */
    protected $protocol = null;
 
    /** @var string|null $textMessage */
    protected $textMessage = null;
 
    /** @var string|null $htmlMessage */
    protected $htmlMessage = null;
 
    /** @var bool $isHTML */
    protected $isHTML = false;
 
    /** @var bool $isTLS */
    protected $isTLS = false;
 
    /** @var array $logs */
    protected $logs = array();
 
    /** @var string $charset */
    protected $charset = 'utf-8';
 
    /** @var array $headers */
    protected $headers = array();
 
    /**
     * Class constructor
     *  -- Set server name, port and timeout values
     *
     * @param string $server
     * @param int $port
     * @param int $connectionTimeout
     * @param int $responseTimeout
     * @param string|null $hostname
     */
    public function __construct($server, $port = 25, $connectionTimeout = 30, $responseTimeout = 8, $hostname = null)
    {
        $this->port = $port;
        $this->server = $server;
        $this->connectionTimeout = $connectionTimeout;
        $this->responseTimeout = $responseTimeout;
        $this->hostname = empty($hostname) ? gethostname() : $hostname;
        $this->setHeader('X-Mailer', 'PHP/' . phpversion());
        $this->setHeader('MIME-Version', '1.0');
    }
 
    /**
     * @param string $key
     * @param mixed|null $value
     * @return Email
     */
    public function setHeader($key, $value = null)
    {
        $this->headers[$key] = $value;
 
        return $this;
    }
 
    /**
     * Add to recipient email address
     *
     * @param string $address
     * @param string|null $name
     * @return Email
     */
    public function addTo($address, $name = null)
    {
        $this->to[] = array($address, $name);
 
        return $this;
    }
 
    /**
     * Add carbon copy email address
     *
     * @param string $address
     * @param string|null $name
     * @return Email
     */
    public function addCc($address, $name = null)
    {
        $this->cc[] = array($address, $name);
 
        return $this;
    }
 
    /**
     * Add blind carbon copy email address
     *
     * @param string $address
     * @param string|null $name
     * @return Email
     */
    public function addBcc($address, $name = null)
    {
        $this->bcc[] = array($address, $name);
 
        return $this;
    }
 
    /**
     * Add email reply to address
     *
     * @param string $address
     * @param string|null $name
     * @return Email
     */
    public function addReplyTo($address, $name = null)
    {
        $this->replyTo[] = array($address, $name);
 
        return $this;
    }
 
    /**
     * Add file attachment
     *
     * @param string $attachment
     * @return Email
     */
    public function addAttachment($attachment)
    {
        if (file_exists($attachment)) {
            $this->attachments[] = $attachment;
        }
 
        return $this;
    }
 
    /**
     * Set SMTP Login authentication
     *
     * @param string $username
     * @param string $password
     * @return Email
     */
    public function setLogin($username, $password)
    {
        $this->username = $username;
        $this->password = $password;
 
        return $this;
    }
 
    /**
     * Get message character set
     *
     * @param string $charset
     * @return Email
     */
    public function setCharset($charset)
    {
        $this->charset = $charset;
 
        return $this;
    }
 
    /**
     * Set SMTP Server protocol
     * -- default value is null (no secure protocol)
     *
     * @param string $protocol
     * @return Email
     */
    public function setProtocol($protocol = null)
    {
        if ($protocol === self::TLS) {
            $this->isTLS = true;
        }
 
        $this->protocol = $protocol;
 
        return $this;
    }
 
    /**
     * Set from email address and/or name
     *
     * @param string $address
     * @param string|null $name
     * @return Email
     */
    public function setFrom($address, $name = null)
    {
        $this->from = array($address, $name);
 
        return $this;
    }
 
    /**
     * Set email subject string
     *
     * @param string $subject
     * @return Email
     */
    public function setSubject($subject)
    {
        $this->subject = $subject;
 
        return $this;
    }
 
    /**
     * Set plain text message body
     *
     * @param string $message
     * @return Email
     */
    public function setTextMessage($message)
    {
        $this->textMessage = $message;
 
        return $this;
    }
 
    /**
     * Set html message body
     *
     * @param string $message
     * @return Email
     */
    public function setHtmlMessage($message)
    {
        $this->htmlMessage = $message;
 
        return $this;
    }
 
    /**
     * Get log array
     * -- contains commands and responses from SMTP server
     *
     * @return array
     */
    public function getLogs()
    {
        return $this->logs;
    }
 
    /**
     * Send email to recipient via mail server
     *
     * @return bool
     */
    public function send()
    {
        $message = null;
        $this->socket = fsockopen(
            $this->getServer(),
            $this->port,
            $errorNumber,
            $errorMessage,
            $this->connectionTimeout
        );
 
        if (empty($this->socket)) {
            return false;
        }
 
        $this->logs['CONNECTION'] = $this->getResponse();
        $this->logs['HELLO'][1] = $this->sendCommand('EHLO ' . $this->hostname);
 
        if ($this->isTLS) {
            $this->logs['STARTTLS'] = $this->sendCommand('STARTTLS');
            stream_socket_enable_crypto($this->socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
            $this->logs['HELLO'][2] = $this->sendCommand('EHLO ' . $this->hostname);
        }
 
        $this->logs['AUTH'] = $this->sendCommand('AUTH LOGIN');
        $this->logs['USERNAME'] = $this->sendCommand(base64_encode($this->username));
        $this->logs['PASSWORD'] = $this->sendCommand(base64_encode($this->password));
        $this->logs['MAIL_FROM'] = $this->sendCommand('MAIL FROM: <' . $this->from[0] . '>');
 
        $recipients = array_merge($this->to, $this->cc, $this->bcc);
        foreach ($recipients as $address) {
            $this->logs['RECIPIENTS'][] = $this->sendCommand('RCPT TO: <' . $address[0] . '>');
        }
 
        $this->setHeader('Date', date('r'));
        $this->setHeader('Subject', $this->subject);
        $this->setHeader('From', $this->formatAddress($this->from));
        $this->setHeader('Return-Path', $this->formatAddress($this->from));
        $this->setHeader('To', $this->formatAddressList($this->to));
 
        if (!empty($this->replyTo)) {
            $this->setHeader('Reply-To', $this->formatAddressList($this->replyTo));
        }
 
        if (!empty($this->cc)) {
            $this->setHeader('Cc', $this->formatAddressList($this->cc));
        }
 
        if (!empty($this->bcc)) {
            $this->setHeader('Bcc', $this->formatAddressList($this->bcc));
        }
 
        $boundary = md5(uniqid(microtime(true), true));
        $this->setHeader('Content-Type', 'multipart/mixed; boundary="mixed-' . $boundary . '"');
 
        if (!empty($this->attachments)) {
            $this->headers['Content-Type'] = 'multipart/mixed; boundary="mixed-' . $boundary . '"';
            $message .= '--mixed-' . $boundary . self::CRLF;
            $message .= 'Content-Type: multipart/alternative; boundary="alt-' . $boundary . '"' . self::CRLF . self::CRLF;
        } else {
            $this->headers['Content-Type'] = 'multipart/alternative; boundary="alt-' . $boundary . '"';
        }
 
        if (!empty($this->textMessage)) {
            $message .= '--alt-' . $boundary . self::CRLF;
            $message .= 'Content-Type: text/plain; charset=' . $this->charset . self::CRLF;
            $message .= 'Content-Transfer-Encoding: base64' . self::CRLF . self::CRLF;
            $message .= chunk_split(base64_encode($this->textMessage)) . self::CRLF;
        }
 
        if (!empty($this->htmlMessage)) {
            $message .= '--alt-' . $boundary . self::CRLF;
            $message .= 'Content-Type: text/html; charset=' . $this->charset . self::CRLF;
            $message .= 'Content-Transfer-Encoding: base64' . self::CRLF . self::CRLF;
            $message .= chunk_split(base64_encode($this->htmlMessage)) . self::CRLF;
        }
 
        $message .= '--alt-' . $boundary . '--' . self::CRLF . self::CRLF;
 
        if (!empty($this->attachments)) {
            foreach ($this->attachments as $attachment) {
                $filename = pathinfo($attachment, PATHINFO_BASENAME);
                $contents = file_get_contents($attachment);
                $type = mime_content_type($attachment);
                if (!$type) {
                    $type = 'application/octet-stream';
                }
 
                $message .= '--mixed-' . $boundary . self::CRLF;
                $message .= 'Content-Type: ' . $type . '; name="' . $filename . '"' . self::CRLF;
                $message .= 'Content-Disposition: attachment; filename="' . $filename . '"' . self::CRLF;
                $message .= 'Content-Transfer-Encoding: base64' . self::CRLF . self::CRLF;
                $message .= chunk_split(base64_encode($contents)) . self::CRLF;
            }
 
            $message .= '--mixed-' . $boundary . '--';
        }
 
        $headers = '';
        foreach ($this->headers as $k => $v) {
            $headers .= $k . ': ' . $v . self::CRLF;
        }
 
        $this->logs['MESSAGE'] = $message;
        $this->logs['HEADERS'] = $headers;
        $this->logs['DATA'][1] = $this->sendCommand('DATA');
        $this->logs['DATA'][2] = $this->sendCommand($headers . self::CRLF . $message . self::CRLF . '.');
        $this->logs['QUIT'] = $this->sendCommand('QUIT');
        fclose($this->socket);
 
        return substr($this->logs['DATA'][2], 0, 3) == self::OK;
    }
 
    /**
     * Get server url
     * -- if set SMTP protocol then prepend it to server
     *
     * @return string
     */
    protected function getServer()
    {
        return ($this->protocol) ? $this->protocol . '://' . $this->server : $this->server;
    }
 
    /**
     * Get Mail Server response
     * @return string
     */
    protected function getResponse()
    {
        $response = '';
        stream_set_timeout($this->socket, $this->responseTimeout);
        while (($line = fgets($this->socket, 515)) !== false) {
            $response .= trim($line) . "\n";
            if (substr($line, 3, 1) == ' ') {
                break;
            }
        }
 
        return trim($response);
    }
 
    /**
     * Send command to mail server
     *
     * @param string $command
     * @return string
     */
    protected function sendCommand($command)
    {
        fputs($this->socket, $command . self::CRLF);
 
        return $this->getResponse();
    }
 
    /**
     * Format email address (with name)
     *
     * @param array $address
     * @return string
     */
    protected function formatAddress($address)
    {
        return (empty($address[1])) ? $address[0] : '"' . addslashes($address[1]) . '" <' . $address[0] . '>';
    }
 
    /**
     * Format email address to list
     *
     * @param array $addresses
     * @return string
     */
    protected function formatAddressList(array $addresses)
    {
        $data = array();
        foreach ($addresses as $address) {
            $data[] = $this->formatAddress($address);
        }
 
        return implode(', ', $data);
    }
}
 
use Snipworks\Smtp\Email;
 
$mail = new Email('server.example.com', 587);
$mail->setProtocol(Email::TLS)
    ->setLogin('sender@example.com', 'SECRET')
    ->setFrom('sender@example.com')
    ->setSubject('Test subject')
    ->setTextMessage('Plain text message')
    ->setHtmlMessage('<strong>HTML Text Message</strong>')
    ->addTo('recipient@example.com');
 
if ($mail->send()) {
    echo 'SMTP Email has been sent' . PHP_EOL;
    exit(0);
}
 
echo 'An error has occurred. Please check the logs below:' . PHP_EOL;
print_r($mail->getLogs());