diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..45832c7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+/.idea/
+/vendor/
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..baa5e0e
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,13 @@
+{
+ "name": "hujuice/smtp",
+ "version": "1.6",
+ "autoload": {
+ "psr-4": {
+ "Hujuice\\Smtp\\": "src/"
+ }
+ },
+ "require": {},
+ "require-dev": {
+ "phpunit/phpunit": "3.7.*"
+ }
+}
\ No newline at end of file
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..8cde319
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,430 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "hash": "1873393e3bd3ecf6579868921cb07339",
+ "packages": [],
+ "packages-dev": [
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "1.2.18",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
+ "reference": "fe2466802556d3fe4e4d1d58ffd3ccfd0a19be0b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "phpunit/php-file-iterator": ">=1.3.0@stable",
+ "phpunit/php-text-template": ">=1.2.0@stable",
+ "phpunit/php-token-stream": ">=1.1.3,<1.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "3.7.*@dev"
+ },
+ "suggest": {
+ "ext-dom": "*",
+ "ext-xdebug": ">=2.0.5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "PHP/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "include-path": [
+ ""
+ ],
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "time": "2014-09-02 10:13:14"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "1.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb",
+ "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "File/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "include-path": [
+ ""
+ ],
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+ "keywords": [
+ "filesystem",
+ "iterator"
+ ],
+ "time": "2013-10-10 15:34:57"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "1.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
+ "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "Text/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "include-path": [
+ ""
+ ],
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+ "keywords": [
+ "template"
+ ],
+ "time": "2014-01-30 17:20:04"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
+ "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "PHP/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "include-path": [
+ ""
+ ],
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "time": "2013-08-02 07:42:54"
+ },
+ {
+ "name": "phpunit/php-token-stream",
+ "version": "1.2.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+ "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32",
+ "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.2-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "PHP/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "include-path": [
+ ""
+ ],
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Wrapper around PHP's tokenizer extension.",
+ "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+ "keywords": [
+ "tokenizer"
+ ],
+ "time": "2014-03-03 05:10:30"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "3.7.38",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/38709dc22d519a3d1be46849868aa2ddf822bcf6",
+ "reference": "38709dc22d519a3d1be46849868aa2ddf822bcf6",
+ "shasum": ""
+ },
+ "require": {
+ "ext-ctype": "*",
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-pcre": "*",
+ "ext-reflection": "*",
+ "ext-spl": "*",
+ "php": ">=5.3.3",
+ "phpunit/php-code-coverage": "~1.2",
+ "phpunit/php-file-iterator": "~1.3",
+ "phpunit/php-text-template": "~1.1",
+ "phpunit/php-timer": "~1.0",
+ "phpunit/phpunit-mock-objects": "~1.2",
+ "symfony/yaml": "~2.0"
+ },
+ "require-dev": {
+ "pear-pear.php.net/pear": "1.9.4"
+ },
+ "suggest": {
+ "phpunit/php-invoker": "~1.1"
+ },
+ "bin": [
+ "composer/bin/phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.7.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "PHPUnit/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "include-path": [
+ "",
+ "../../symfony/yaml/"
+ ],
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "http://www.phpunit.de/",
+ "keywords": [
+ "phpunit",
+ "testing",
+ "xunit"
+ ],
+ "time": "2014-10-17 09:04:17"
+ },
+ {
+ "name": "phpunit/phpunit-mock-objects",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
+ "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/5794e3c5c5ba0fb037b11d8151add2a07fa82875",
+ "reference": "5794e3c5c5ba0fb037b11d8151add2a07fa82875",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3",
+ "phpunit/php-text-template": ">=1.1.1@stable"
+ },
+ "suggest": {
+ "ext-soap": "*"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "PHPUnit/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "include-path": [
+ ""
+ ],
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sb@sebastian-bergmann.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Mock Object library for PHPUnit",
+ "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+ "keywords": [
+ "mock",
+ "xunit"
+ ],
+ "time": "2013-01-13 10:24:48"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v2.6.1",
+ "target-dir": "Symfony/Component/Yaml",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/Yaml.git",
+ "reference": "3346fc090a3eb6b53d408db2903b241af51dcb20"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/Yaml/zipball/3346fc090a3eb6b53d408db2903b241af51dcb20",
+ "reference": "3346fc090a3eb6b53d408db2903b241af51dcb20",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Symfony\\Component\\Yaml\\": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Symfony Community",
+ "homepage": "http://symfony.com/contributors"
+ },
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "Symfony Yaml Component",
+ "homepage": "http://symfony.com",
+ "time": "2014-12-02 20:19:20"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "platform": [],
+ "platform-dev": []
+}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..e2ec3a4
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ test
+
+
+
+ .:./test
+
+
\ No newline at end of file
diff --git a/smtp.php b/src/Smtp.php
similarity index 65%
rename from smtp.php
rename to src/Smtp.php
index 33f6847..68cb6d8 100644
--- a/smtp.php
+++ b/src/Smtp.php
@@ -21,16 +21,19 @@
* @author Sergio Vaccaro
* @copyright Copyright (c) Sergio Vaccaro
* @license http://www.gnu.org/licenses/gpl-3.0.txt GPLv3
- * @version 1.1
+ * @version 1.3
*/
+namespace Hujuice\Smtp;
+use Exception;
+
/**
* Rich SMTP client
*
* @package SMTP
* @link http://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol Documentation
*/
-class smtp
+class Smtp
{
/**
* New line character
@@ -207,6 +210,8 @@ class smtp
'charset' => 'UTF-8'
);
+ protected $_body = null;
+
/**
* File attachments
*
@@ -228,11 +233,16 @@ class smtp
*/
protected $_log = '';
+ protected $_pipelining = true;
+
+ protected $_pipelinedCommands = array();
+
/**
* Charset encoding
*
* @see http://www.pcvr.nl/tcpip/smtp_sim.htm
* @param string $string
+ * @return string
*/
protected function _encode($string)
{
@@ -249,7 +259,7 @@ protected function _encode($string)
* @param string $dest
* @param string $destName
* @param array $class
- * @throw Exception
+ * @throws Exception
*/
protected function _recipients($dest, $destName, $class)
{
@@ -275,32 +285,57 @@ protected function _recipients($dest, $destName, $class)
throw new Exception('Wrong recipient');
}
+ protected function _readResponse($expected)
+ {
+ $response = '';
+ while (($line = fgets($this->_smtp)) !== false) {
+ $response .= $line;
+ if ($line[3] != '-') {
+ break;
+ }
+ }
+ $this->_log .= $response;
+
+ if (substr($response, 0, 3) != $expected) {
+ throw new Exception("Unexpected response. Expected {$expected}. Here is the dialog dump:\n{$this->_log}");
+ }
+
+ return $response;
+ }
+
/**
* Perform a request/response exchange
*
* @param string $request
* @param string $expect The expected status code
* @return string
- * @throw Exception
+ * @throws Exception
*/
protected function _dialog($request, $expect)
{
$this->_log .= $request . PHP_EOL;
- fwrite ($this->_smtp, $request . self::NL);
- $response = fgets($this->_smtp);
-
- $this->_log .= $response . PHP_EOL;
-
- if (substr($response, 0, 3) != $expect)
- throw new Exception('Message "' . $request . '" NOT accepted! Here is the dialog dump:' . PHP_EOL . $this->_log);
+ fwrite($this->_smtp, $request . self::NL);
+
+ if ($this->_pipelining) {
+ // is pipelinable command?
+ if (in_array(substr($request, 0, 4), array('RSET', 'MAIL', 'SEND', 'SOML', 'SAML', 'RCPT'))) {
+ $this->_pipelinedCommands[] = $expect;
+ return null;
+ } else {
+ while ($this->_pipelinedCommands) {
+ $_expected = array_shift($this->_pipelinedCommands);
+ $this->_readResponse($_expected);
+ }
+ }
+ }
- return $response;
+ return $this->_readResponse($expect);
}
/**
* Connection to the SMTP server
- * @throw Exception
+ * @throws Exception
*/
public function _connect()
{
@@ -316,7 +351,8 @@ public function _connect()
// HELO
$sender = explode('@', $this->_from['address']);
- $this->_dialog('HELO ' . $sender[1], self::OK);
+ $ehlo = $this->_dialog('EHLO ' . $sender[1], self::OK);
+ $this->_pipelining = preg_match('~250[\s-]pipelining~i', $ehlo);
// Auth
if ($this->_user && $this->_pass)
@@ -343,7 +379,7 @@ public function _connect()
* @param string $host
* @param integer $port
* @param integer $timeout
- * @throw Exception
+ * @throws Exception
*/
public function __construct($host, $port = 25 , $timeout = 3)
{
@@ -364,11 +400,12 @@ public function __construct($host, $port = 25 , $timeout = 3)
*/
public function __destruct()
{
- // Quit
- $this->_dialog('QUIT', self::BYE);
- if ($this->_smtp)
+ if ($this->_smtp) {
+ // Quit
+ $this->_dialog('QUIT', self::BYE);
fclose($this->_smtp);
+ }
}
/**
@@ -491,6 +528,7 @@ public function bcc($bcc = null, $bccName = '')
* Priority
*
* @param integer $priority
+ * @throws Exception
* @return integer
*/
public function priority($priority = null)
@@ -509,7 +547,8 @@ public function priority($priority = null)
/**
* Custom header
*
- * @param string $header
+ * @param string $name
+ * @param string $value
* @return array
*/
public function header($name = null, $value = null)
@@ -536,6 +575,8 @@ public function subject($subject = null)
* Text
*
* @param string $text
+ * @param string $content_type
+ * @param string $charset
* @return string
*/
public function text($text = null, $content_type = 'text/plain', $charset = 'utf-8')
@@ -543,14 +584,19 @@ public function text($text = null, $content_type = 'text/plain', $charset = 'utf
if (null !== $text)
{
$this->_text = array(
- 'body' => str_replace("\n", self::NL, (string) $text),
- 'Content-Type' => $content_type,
- 'charset' => $charset
- );
+ 'body' => str_replace("\n", self::NL, (string) $text),
+ 'Content-Type' => $content_type,
+ 'charset' => $charset
+ );
}
return $this->_text;
}
+ public function body($body)
+ {
+ $this->_body = $body;
+ }
+
/**
* Attachment from file
*
@@ -558,8 +604,10 @@ public function text($text = null, $content_type = 'text/plain', $charset = 'utf
* @link http://en.wikipedia.org/wiki/MIME#Multipart_messages
* @link http://support.mozilla.org/it/questions/746116
* @param string $path
+ * @param string $name
* @param string $content_type
* @param string $charset Will be used for text/* only
+ * @throws Exception
* @return array
*/
public function attachment($path = null, $name = '', $content_type = 'application/octet-stream', $charset = 'utf-8')
@@ -567,10 +615,10 @@ public function attachment($path = null, $name = '', $content_type = 'applicatio
if (is_readable($path))
{
$attachment = array(
- 'path' => (string) $path,
- 'Content-Type' => (string) $content_type,
- 'charset' => (string) $charset
- );
+ 'path' => (string) $path,
+ 'Content-Type' => (string) $content_type,
+ 'charset' => (string) $charset
+ );
$name || ($name = pathinfo($path, PATHINFO_BASENAME));
@@ -599,10 +647,10 @@ public function raw($content = null, $name = '', $content_type = 'text/plain', $
if ($content)
{
$attachment = array(
- 'content' => (string) $content,
- 'Content-Type' => (string) $content_type,
- 'charset' => (string) $charset
- );
+ 'content' => (string) $content,
+ 'Content-Type' => (string) $content_type,
+ 'charset' => (string) $charset
+ );
if (empty($name))
$name = time() . '-' . mt_rand();
@@ -623,6 +671,7 @@ public function clear()
$this->_headers = array();
$this->_attachments = array();
$this->_raw = array();
+ $this->_body = null;
}
/**
@@ -630,7 +679,7 @@ public function clear()
*
* @see http://www.pcvr.nl/tcpip/smtp_sim.htm
* @return string
- * @throw Exception
+ * @throws Exception
*/
public function send()
{
@@ -641,11 +690,13 @@ public function send()
if (empty($this->_to) && empty($this->_cc) && empty($this->_bcc))
throw new Exception('No recipients');
- if (empty($this->_subject)) // Net Ecology
- throw new Exception('No subject');
+ if (empty($this->_body)) {
+ if (empty($this->_subject)) // Net Ecology
+ throw new Exception('No subject');
- if (empty($this->_text))
- throw new Exception('No message text');
+ if (empty($this->_text))
+ throw new Exception('No message text');
+ }
// Connection
$this->_connect();
@@ -668,128 +719,132 @@ public function send()
// Data
$this->_dialog('DATA', self::DATAOK);
- // Message
- $message = '';
-
- // From
- if (empty($this->_from['name']))
- $message .= 'From: <' . $this->_from['address'] . '>' . self::NL;
- else
- $message .= 'From: "' . $this->_encode($this->_from['name']) . '"<' . $this->_from['address'] . '>' . self::NL;
+ if ($this->_body) {
+ $message = $this->_body . self::NL;
+ } else {
+ // Message
+ $message = '';
- // Reply to
- if (!empty($this->_replyTo))
- {
- if (empty($this->_replyTo['name']))
- $message .= 'Reply-To: <' . $this->_replyTo['address'] . '>' . self::NL;
+ // From
+ if (empty($this->_from['name']))
+ $message .= 'From: <' . $this->_from['address'] . '>' . self::NL;
else
- $message .= 'Reply-To: "' . $this->_encode($this->_replyTo['name']) . '"<' . $this->_replyTo['address'] . '>' . self::NL;
- }
+ $message .= 'From: "' . $this->_encode($this->_from['name']) . '"<' . $this->_from['address'] . '>' . self::NL;
- // To
- foreach ($this->_to as $name => $rcpt)
- {
- if (is_integer($name))
- $message .= 'To: <' . $rcpt . '>' . self::NL;
- else
- $message .= 'To: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL;
- }
+ // Reply to
+ if (!empty($this->_replyTo))
+ {
+ if (empty($this->_replyTo['name']))
+ $message .= 'Reply-To: <' . $this->_replyTo['address'] . '>' . self::NL;
+ else
+ $message .= 'Reply-To: "' . $this->_encode($this->_replyTo['name']) . '"<' . $this->_replyTo['address'] . '>' . self::NL;
+ }
- // Cc
- foreach ($this->_cc as $name => $rcpt)
- {
- if (is_integer($name))
- $message .= 'Cc: <' . $rcpt . '>' . self::NL;
- else
- $message .= 'Cc: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL;
- }
+ // To
+ foreach ($this->_to as $name => $rcpt)
+ {
+ if (is_integer($name))
+ $message .= 'To: <' . $rcpt . '>' . self::NL;
+ else
+ $message .= 'To: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL;
+ }
- // Bcc
- foreach ($this->_bcc as $name => $rcpt)
- {
- if (is_integer($name))
- $message .= 'Bcc: <' . $rcpt . '>' . self::NL;
- else
- $message .= 'Bcc: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL;
- }
+ // Cc
+ foreach ($this->_cc as $name => $rcpt)
+ {
+ if (is_integer($name))
+ $message .= 'Cc: <' . $rcpt . '>' . self::NL;
+ else
+ $message .= 'Cc: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL;
+ }
- // Priority
- if ($this->_priority)
- $message .= 'X-Priority: ' . $this->_priority . self::NL;
+ // Bcc
+ foreach ($this->_bcc as $name => $rcpt)
+ {
+ if (is_integer($name))
+ $message .= 'Bcc: <' . $rcpt . '>' . self::NL;
+ else
+ $message .= 'Bcc: "' . $this->_encode($name) . '"<' . $rcpt . '>' . self::NL;
+ }
- // Mailer
- $message .= 'X-mailer: ' . self::MAILER . self::NL;
- $message .= 'X-mailer-author: ' . self::MAILER_AUTHOR . self::NL;
+ // Priority
+ if ($this->_priority)
+ $message .= 'X-Priority: ' . $this->_priority . self::NL;
- // Custom headers
- foreach ($this->_headers as $name => $value)
- $message .= $name . ': ' . $value. self::NL;
+ // Mailer
+ $message .= 'X-mailer: ' . self::MAILER . self::NL;
+ $message .= 'X-mailer-author: ' . self::MAILER_AUTHOR . self::NL;
- // Date
- $message .= 'Date: ' . date('r') . self::NL;
+ // Custom headers
+ foreach ($this->_headers as $name => $value)
+ $message .= $name . ': ' . $value. self::NL;
- // Subject
- $message .= 'Subject: ' . $this->_encode($this->_subject) . self::NL;
+ // Date
+ $message .= 'Date: ' . date('r') . self::NL;
- // Message
- /*
- The message will containt text and attachments.
- This implementation consider the multipart/mixed method only.
- http://en.wikipedia.org/wiki/MIME#Multipart_messages
- */
- if ($this->_attachments || $this->_raw)
- {
- $separator = hash('sha256', time());
- $message .= 'MIME-Version: 1.0' . self::NL;
- $message .= 'Content-Type: multipart/mixed; boundary=' . $separator . self::NL;
- $message .= self::NL;
- $message .= 'This is a message with multiple parts in MIME format.' . self::NL;
- $message .= '--' . $separator . self::NL;
- $message .= 'Content-Type: ' . $this->_text['Content-Type'] . '; charset=' . $this->_text['charset'] . self::NL;
- $message .= self::NL;
- $message .= $this->_text['body'] . self::NL;
- foreach ($this->_attachments as $name => $attach)
+ // Subject
+ $message .= 'Subject: ' . $this->_encode($this->_subject) . self::NL;
+
+ // Message
+ /*
+ The message will contain text and attachments.
+ This implementation consider the multipart/mixed method only.
+ http://en.wikipedia.org/wiki/MIME#Multipart_messages
+ */
+ if ($this->_attachments || $this->_raw)
{
+ $separator = hash('sha256', time());
+ $message .= 'MIME-Version: 1.0' . self::NL;
+ $message .= 'Content-Type: multipart/mixed; boundary=' . $separator . self::NL;
+ $message .= self::NL;
+ $message .= 'This is a message with multiple parts in MIME format.' . self::NL;
$message .= '--' . $separator . self::NL;
- $message .= 'Content-Disposition: attachment; filename=' . $name . '; modification-date="' . date('r', filemtime($attach['path'])) . '"' . self::NL;
- if (substr($attach['Content-Type'], 0, 5) == 'text/')
+ $message .= 'Content-Type: ' . $this->_text['Content-Type'] . '; charset=' . $this->_text['charset'] . self::NL;
+ $message .= self::NL;
+ $message .= $this->_text['body'] . self::NL;
+ foreach ($this->_attachments as $name => $attach)
{
- $message .= 'Content-Type: ' . $attach['Content-Type'] . '; charset=' . $attach['charset'] . self::NL;
- $message .= self::NL;
- $message .= file_get_contents($attach['path']) . self::NL;
+ $message .= '--' . $separator . self::NL;
+ $message .= 'Content-Disposition: attachment; filename=' . $name . '; modification-date="' . date('r', filemtime($attach['path'])) . '"' . self::NL;
+ if (substr($attach['Content-Type'], 0, 5) == 'text/')
+ {
+ $message .= 'Content-Type: ' . $attach['Content-Type'] . '; charset=' . $attach['charset'] . self::NL;
+ $message .= self::NL;
+ $message .= file_get_contents($attach['path']) . self::NL;
+ }
+ else
+ {
+ $message .= 'Content-Type: ' . $attach['Content-Type'] . self::NL;
+ $message .= 'Content-Transfer-Encoding: base64' . self::NL;
+ $message .= self::NL;
+ $message .= base64_encode(file_get_contents($attach['path'])) . self::NL;
+ }
}
- else
+ foreach ($this->_raw as $name => $raw)
{
- $message .= 'Content-Type: ' . $attach['Content-Type'] . self::NL;
- $message .= 'Content-Transfer-Encoding: base64' . self::NL;
- $message .= self::NL;
- $message .= base64_encode(file_get_contents($attach['path'])) . self::NL;
+ $message .= '--' . $separator . self::NL;
+ $message .= 'Content-Disposition: attachment; filename=' . $name . '; modification-date="' . date('r') . '"' . self::NL;
+ if (substr($raw['Content-Type'], 0, 5) == 'text/')
+ {
+ $message .= 'Content-Type: ' . $raw['Content-Type'] . '; charset=' . $raw['charset'] . self::NL;
+ $message .= self::NL;
+ $message .= $raw['content'] . self::NL;
+ }
+ else
+ {
+ $message .= 'Content-Type: ' . $raw['Content-Type'] . self::NL;
+ $message .= 'Content-Transfer-Encoding: base64' . self::NL;
+ $message .= self::NL;
+ $message .= base64_encode($raw['content']) . self::NL;
+ }
}
+ $message .= '--' . $separator . '--' . self::NL;
}
- foreach ($this->_raw as $name => $raw)
+ else
{
- $message .= '--' . $separator . self::NL;
- $message .= 'Content-Disposition: attachment; filename=' . $name . '; modification-date="' . date('r') . '"' . self::NL;
- if (substr($raw['Content-Type'], 0, 5) == 'text/')
- {
- $message .= 'Content-Type: ' . $raw['Content-Type'] . '; charset=' . $raw['charset'] . self::NL;
- $message .= self::NL;
- $message .= $raw['content'] . self::NL;
- }
- else
- {
- $message .= 'Content-Type: ' . $raw['Content-Type'] . self::NL;
- $message .= 'Content-Transfer-Encoding: base64' . self::NL;
- $message .= self::NL;
- $message .= base64_encode($raw['content']) . self::NL;
- }
+ $message .= 'Content-Type: ' . $this->_text['Content-Type'] . '; charset=' . $this->_text['charset'] . self::NL;
+ $message .= self::NL . $this->_text['body'] . self::NL;
}
- $message .= '--' . $separator . '--' . self::NL;
- }
- else
- {
- $message .= 'Content-Type: ' . $this->_text['Content-Type'] . '; charset=' . $this->_text['charset'] . self::NL;
- $message .= self::NL . $this->_text['body'] . self::NL;
}
$message .= '.'; // The _dialog function below will add self::NL;
diff --git a/test/SmtpTest.php b/test/SmtpTest.php
new file mode 100644
index 0000000..8a39266
--- /dev/null
+++ b/test/SmtpTest.php
@@ -0,0 +1,22 @@
+assertInstanceOf(Smtp::class, $smtp);
+ }
+
+ public function testSend()
+ {
+ $smtp = new Smtp('localhost');
+ $smtp->from('foo@example.com');
+ $smtp->to('bar@example.com');
+ $smtp->subject('Test');
+ $smtp->text('Test');
+ $res = $smtp->send();
+ $this->assertStringStartsWith('Message queued for delivery', $res);
+ }
+}