diff --git a/README.md b/README.md index 3504e69..c9e63b5 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ adtMailer: logDir: %logDir%/adt_mailer # if recipient is suppressed, this address receives notification and delist link - # can be either static string or method, required + # can be either an email address, url or a callback returning an email address or url suppressionControlAddress: @App\Model\SuppressionControl::decide ``` diff --git a/composer.json b/composer.json index 1c0a03e..8219025 100644 --- a/composer.json +++ b/composer.json @@ -11,12 +11,11 @@ ], "minimum-stability": "stable", "require": { - "php": ">=5.5.0", - "ext-curl": "*", - "nette/mail": "^2.3", + "php": ">=7.4", + "nette/mail": "^2.3 | ^3.0 | ^4.0", "tracy/tracy": "^2.3", - "nette/di": "^2.3", - "nette/utils": "^2.3" + "nette/di": "^2.3 || ^3.0", + "guzzlehttp/guzzle": "^6.3|^7.0" }, "require-dev": { }, diff --git a/src/DI/AdtMailerExtension.php b/src/DI/AdtMailerExtension.php index 49a7745..d9d3af7 100644 --- a/src/DI/AdtMailerExtension.php +++ b/src/DI/AdtMailerExtension.php @@ -44,15 +44,15 @@ public function loadConfiguration() { ->setAutowired($config['autowireMailer']); } - public function validateConfig(array $expected, array $config = NULL, $name = NULL) { + public function validateConfig(array $expected, ?array $config = NULL, $name = NULL): array { $config = parent::validateConfig($expected, $config, $name); if (empty($config['remote']['api'])) { throw new \Nette\UnexpectedValueException('Specify remote API endpoint.'); } - if (empty($config['remote']['key']) || !(is_string($config['remote']['key']) || is_callable($config['remote']['key']))) { - throw new \Nette\UnexpectedValueException('Specify authentication key as string or method (e.g. @ServiceClass::method).'); + if (empty($config['remote']['key']) || !(is_string($config['remote']['key']) || is_array($config['remote']['key']))) { + throw new \Nette\UnexpectedValueException('Specify authentication key as string or method (e.g. [@ServiceClass, method]).'); } if (!in_array($config['error']['mode'], static::errorModes(), TRUE)) { @@ -65,12 +65,7 @@ public function validateConfig(array $expected, array $config = NULL, $name = NU throw new \Nette\UnexpectedValueException('Specify mail log directory.'); } - if (empty($config['suppressionControlAddress']) || !(is_string($config['suppressionControlAddress']) || is_callable($config['suppressionControlAddress']))) { - throw new \Nette\UnexpectedValueException('Specify suppression control address as string or method (e.g. @ServiceClass::method).'); - } - return $config; } - -} \ No newline at end of file +} diff --git a/src/Services/Api.php b/src/Services/Api.php index 68689ac..ce748de 100644 --- a/src/Services/Api.php +++ b/src/Services/Api.php @@ -3,15 +3,13 @@ namespace ADT\Mailer\Services; use ADT\Mailer\DI\AdtMailerExtension; +use GuzzleHttp\Client; class Api { /** @var array */ protected $config; - /** @var resource */ - protected $curl; - /** @var \Tracy\ILogger */ protected $logger; @@ -52,73 +50,48 @@ protected function serializeMessage(\Nette\Mail\Message $mail) { $result[$header] = $mail->getHeader(ucfirst($header)); } + if ($mail->getHeader('Reply-To') !== null) { + $result['reply-to'] = $mail->getHeader('Reply-To'); + } + return $result; } + /** + * @param \Nette\Mail\Message $mail + * @return bool + * @throws \Exception + */ public function send(\Nette\Mail\Message $mail) { - $this->curl = curl_init(); - // set remote URL + $postData = \GuzzleHttp\json_encode($this->serializeMessage($mail)); $endPoint = rtrim($this->config['remote']['api'], '/'); - curl_setopt( - $this->curl, - CURLOPT_URL, - $endPoint . '/mail/send?key=' . $this->getRemoteKey($mail) - ); - - // do not wait more than 3s - curl_setopt($this->curl, CURLOPT_TIMEOUT_MS, 3000); - - if (PHP_VERSION_ID >= 50600) { - // follow redirects (throws 'CURLOPT_FOLLOWLOCATION cannot be activated when safe_mode is enabled or an open_basedir is set' on 5.5) - curl_setopt($this->curl, CURLOPT_FOLLOWLOCATION, TRUE); - } - - // disable cache, set content type, keep alive - curl_setopt( - $this->curl, - CURLOPT_HTTPHEADER, - [ - 'Cache-Control: no-cache', - 'Content-Type: application/octet-stream', - 'Connection: Keep-Alive', - 'Keep-Alive: 300', - 'Expect:', // do not send Expect: 100-continue - ] - ); - // do not display result - curl_setopt($this->curl, CURLOPT_RETURNTRANSFER, TRUE); - - $postData = \Nette\Utils\Json::encode($this->serializeMessage($mail)); - - // set message - curl_setopt($this->curl, CURLOPT_POSTFIELDS, $postData); - - // send message - $response = curl_exec($this->curl); - $httpCode = curl_getinfo($this->curl, CURLINFO_HTTP_CODE); + $client = new Client; try { - $status = \Nette\Utils\Json::decode($response); - - // success check - if (substr($httpCode, 0, 1) === '2' && $status->status === 'ok') { - return; + $client->request("POST", $endPoint . '/mail/send?key=' . $this->getRemoteKey($mail), [ + 'headers' => [ + 'Cache-Control'=> 'no-cache', + 'Content-Type' => 'application/octet-stream', + 'Connection' => 'Keep-Alive', + 'Keep-Alive' => '300', + 'Expect' => NULL, // do not send Expect: 100-continue + ], + "body" => $postData, + "timeout" => 3, + ]); + + return TRUE; + + } catch (\Exception $e) { + + if ($this->config['error']['mode'] === AdtMailerExtension::ERROR_MODE_EXCEPTION) { + throw $e; } - } catch (\Nette\Utils\JsonException $e) { - // error - } - $error = 'Could not transfer mail to remote server (' . ( - !empty($status) - ? $status->error - : $httpCode . ' ' . curl_error($this->curl) - ) . ').'; + $error = 'Could not transfer mail to remote server (' . $e->getMessage() . ').'; - if ($this->config['error']['mode'] === AdtMailerExtension::ERROR_MODE_EXCEPTION) { - throw new ApiException($error); - } else { // create log directory if (!file_exists($this->config['error']['logDir'])) { mkdir($this->config['error']['logDir'], 0777, TRUE); @@ -130,9 +103,11 @@ public function send(\Nette\Mail\Message $mail) { // send report $this->logger->log($error . PHP_EOL . PHP_EOL . $mailFile, \Tracy\ILogger::EXCEPTION); + + return FALSE; } } } class ApiException extends \Nette\IOException { -} \ No newline at end of file +} diff --git a/src/Services/Mailer.php b/src/Services/Mailer.php index c7696e3..c2d0459 100644 --- a/src/Services/Mailer.php +++ b/src/Services/Mailer.php @@ -13,8 +13,8 @@ public function __construct(Api $api) { $this->apiService = $api; } - function send(Message $mail) { + function send(Message $mail): void { $this->apiService->send($mail); } -} \ No newline at end of file +}