Initial commit

This commit is contained in:
ftc
2023-05-17 23:24:39 +03:00
commit 696600086f
5571 changed files with 2035224 additions and 0 deletions
+163
View File
@@ -0,0 +1,163 @@
<?php
/**
* @link https://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license https://www.yiiframework.com/license/
*/
namespace yii\symfonymailer;
use Yii;
use Psr\Log\LoggerInterface;
final class Logger implements LoggerInterface
{
/**
* System is unusable.
*
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*/
public function emergency($message, array $context = []): void
{
Yii::getLogger()->log($message, \yii\log\Logger::LEVEL_ERROR, __METHOD__);
}
/**
* Action must be taken immediately.
*
* Example: Entire website down, database unavailable, etc. This should
* trigger the SMS alerts and wake you up.
*
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*/
public function alert($message, array $context = []): void
{
Yii::getLogger()->log($message, \yii\log\Logger::LEVEL_ERROR, __METHOD__);
}
/**
* Critical conditions.
*
* Example: Application component unavailable, unexpected exception.
*
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*/
public function critical($message, array $context = []): void
{
Yii::getLogger()->log($message, \yii\log\Logger::LEVEL_ERROR, __METHOD__);
}
/**
* Runtime errors that do not require immediate action but should typically
* be logged and monitored.
*
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*/
public function error($message, array $context = []): void
{
Yii::getLogger()->log($message, \yii\log\Logger::LEVEL_ERROR, __METHOD__);
}
/**
* Exceptional occurrences that are not errors.
*
* Example: Use of deprecated APIs, poor use of an API, undesirable things
* that are not necessarily wrong.
*
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*/
public function warning($message, array $context = []): void
{
Yii::getLogger()->log($message, \yii\log\Logger::LEVEL_WARNING, __METHOD__);
}
/**
* Normal but significant events.
*
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*/
public function notice($message, array $context = []): void
{
Yii::getLogger()->log($message, \yii\log\Logger::LEVEL_WARNING, __METHOD__);
}
/**
* Interesting events.
*
* Example: User logs in, SQL logs.
*
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*/
public function info($message, array $context = []): void
{
Yii::getLogger()->log($message, \yii\log\Logger::LEVEL_INFO, __METHOD__);
}
/**
* Detailed debug information.
*
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*/
public function debug($message, array $context = []): void
{
Yii::getLogger()->log($message, \yii\log\Logger::LEVEL_INFO, __METHOD__);
}
/**
* Logs with an arbitrary level.
*
* @param mixed $level
* @param string|\Stringable $message
* @param mixed[] $context
*
* @return void
*
* @throws \Psr\Log\InvalidArgumentException
*/
public function log($level, $message, array $context = []): void
{
switch ($level) {
case 'error':
case 'critical':
case 'alert':
case 'emergency':
$level = \yii\log\Logger::LEVEL_ERROR;
break;
case 'notice':
case 'warning':
$level = \yii\log\Logger::LEVEL_WARNING;
break;
case 'debug':
case 'info':
$level = \yii\log\Logger::LEVEL_INFO;
break;
default:
$level = \yii\log\Logger::LEVEL_INFO;
}
Yii::getLogger()->log($message, $level, __METHOD__);
}
}
+217
View File
@@ -0,0 +1,217 @@
<?php
/**
* @link https://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license https://www.yiiframework.com/license/
*/
namespace yii\symfonymailer;
use RuntimeException;
use Symfony\Component\Mailer\Mailer as SymfonyMailer;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mailer\Transport\Dsn;
use Symfony\Component\Mailer\Transport\TransportInterface;
use Symfony\Component\Mime\Crypto\DkimSigner;
use Symfony\Component\Mime\Crypto\SMimeEncrypter;
use Symfony\Component\Mime\Crypto\SMimeSigner;
use Yii;
use yii\base\InvalidConfigException;
use yii\mail\BaseMailer;
class Mailer extends BaseMailer
{
/**
* @var string message default class name.
*/
public $messageClass = Message::class;
private ?SymfonyMailer $symfonyMailer = null;
private ?SMimeEncrypter $encryptor = null;
/**
* @var DkimSigner|SMimeSigner|null
*/
private $signer = null;
private array $dkimSignerOptions = [];
/**
* @var TransportInterface|array Symfony transport instance or its array configuration.
*/
private $_transport = [];
/**
* @var bool whether to enable writing of the Mailer internal logs using Yii log mechanism.
* If enabled [[Logger]] plugin will be attached to the [[transport]] for this purpose.
* @see Logger
*/
public bool $enableMailerLogging = false;
/**
* Creates Symfony mailer instance.
* @return SymfonyMailer mailer instance.
*/
private function createSymfonyMailer(): SymfonyMailer
{
return new SymfonyMailer($this->getTransport());
}
/**
* @return SymfonyMailer Swift mailer instance
*/
public function getSymfonyMailer(): SymfonyMailer
{
if (!is_object($this->symfonyMailer)) {
$this->symfonyMailer = $this->createSymfonyMailer();
}
return $this->symfonyMailer;
}
/**
* @param array|TransportInterface $transport
* @throws InvalidConfigException on invalid argument.
*/
public function setTransport($transport): void
{
if (!is_array($transport) && !$transport instanceof TransportInterface) {
throw new InvalidConfigException('"' . get_class($this) . '::transport" should be either object or array, "' . gettype($transport) . '" given.');
}
if ($transport instanceof TransportInterface) {
$this->_transport = $transport;
} elseif (is_array($transport)) {
$this->_transport = $this->createTransport($transport);
}
$this->symfonyMailer = null;
}
/**
* @return TransportInterface
*/
public function getTransport(): TransportInterface
{
if (!is_object($this->_transport)) {
$this->_transport = $this->createTransport($this->_transport);
}
return $this->_transport;
}
private function createTransport(array $config = []): TransportInterface
{
if (array_key_exists('enableMailerLogging', $config)) {
$this->enableMailerLogging = $config['enableMailerLogging'];
unset($config['enableMailerLogging']);
}
$logger = null;
if ($this->enableMailerLogging) {
$logger = new Logger();
}
$defaultFactories = Transport::getDefaultFactories(null, null, $logger);
$transportObj = new Transport($defaultFactories);
if (array_key_exists('dsn', $config)) {
$transport = $transportObj->fromString($config['dsn']);
} elseif(array_key_exists('scheme', $config) && array_key_exists('host', $config)) {
$dsn = new Dsn(
$config['scheme'],
$config['host'],
$config['username'] ?? '',
$config['password'] ?? '',
$config['port'] ?? '',
$config['options'] ?? [],
);
$transport = $transportObj->fromDsnObject($dsn);
} else {
throw new InvalidConfigException('Transport configuration array must contain either "dsn", or "scheme" and "host" keys.');
}
return $transport;
}
/**
* Returns a new instance with the specified encryptor.
*
* @param SMimeEncrypter $encryptor The encryptor instance.
*
* @see https://symfony.com/doc/current/mailer.html#encrypting-messages
*
* @return self
*/
public function withEncryptor(SMimeEncrypter $encryptor): self
{
$new = clone $this;
$new->encryptor = $encryptor;
return $new;
}
/**
* Returns a new instance with the specified signer.
*
* @param DkimSigner|object|SMimeSigner $signer The signer instance.
* @param array $options The options for DKIM signer {@see DkimSigner}.
*
* @throws RuntimeException If the signer is not an instance of {@see DkimSigner} or {@see SMimeSigner}.
*
* @see https://symfony.com/doc/current/mailer.html#signing-messages
*
* @return self
*/
public function withSigner(object $signer, array $options = []): self
{
$new = clone $this;
if ($signer instanceof DkimSigner) {
$new->signer = $signer;
$new->dkimSignerOptions = $options;
return $new;
}
if ($signer instanceof SMimeSigner) {
$new->signer = $signer;
return $new;
}
throw new RuntimeException(sprintf(
'The signer must be an instance of "%s" or "%s". The "%s" instance is received.',
DkimSigner::class,
SMimeSigner::class,
get_class($signer),
));
}
/**
* {@inheritDoc}
*
* @throws TransportExceptionInterface If sending failed.
*/
protected function sendMessage($message): bool
{
if (!($message instanceof Message)) {
throw new RuntimeException(sprintf(
'The message must be an instance of "%s". The "%s" instance is received.',
Message::class,
get_class($message),
));
}
$message = $message->getSymfonyEmail();
if ($this->encryptor !== null) {
$message = $this->encryptor->encrypt($message);
}
if ($this->signer !== null) {
$message = $this->signer instanceof DkimSigner
? $this->signer->sign($message, $this->dkimSignerOptions)
: $this->signer->sign($message)
;
}
try {
$this->getSymfonyMailer()->send($message);
} catch (\Exception $exception) {
Yii::getLogger()->log($exception->getMessage(), \yii\log\Logger::LEVEL_ERROR, __METHOD__);
return false;
}
return true;
}
}
+373
View File
@@ -0,0 +1,373 @@
<?php
/**
* @link https://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license https://www.yiiframework.com/license/
*/
namespace yii\symfonymailer;
use DateTimeImmutable;
use DateTimeInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Header\HeaderInterface;
use yii\mail\BaseMessage;
class Message extends BaseMessage
{
private Email $email;
private string $charset = 'utf-8';
public function __construct($config = [])
{
$this->email = new Email();
parent::__construct($config);
}
public function __clone()
{
$this->email = clone $this->email;
}
public function getCharset(): string
{
return $this->charset;
}
public function setCharset($charset): self
{
$this->charset = $charset;
return $this;
}
public function getFrom()
{
return $this->convertAddressesToStrings($this->email->getFrom());
}
public function setFrom($from): self
{
$this->email->from(...$this->convertStringsToAddresses($from));
return $this;
}
public function getTo()
{
return $this->convertAddressesToStrings($this->email->getTo());
}
public function setTo($to): self
{
$this->email->to(...$this->convertStringsToAddresses($to));
return $this;
}
public function getReplyTo()
{
return $this->convertAddressesToStrings($this->email->getReplyTo());
}
public function setReplyTo($replyTo): self
{
$this->email->replyTo(...$this->convertStringsToAddresses($replyTo));
return $this;
}
public function getCc()
{
return $this->convertAddressesToStrings($this->email->getCc());
}
public function setCc($cc): self
{
$this->email->cc(...$this->convertStringsToAddresses($cc));
return $this;
}
public function getBcc()
{
return $this->convertAddressesToStrings($this->email->getBcc());
}
public function setBcc($bcc): self
{
$this->email->bcc(...$this->convertStringsToAddresses($bcc));
return $this;
}
public function getSubject(): string
{
return (string) $this->email->getSubject();
}
public function setSubject($subject): self
{
$this->email->subject($subject);
return $this;
}
public function getDate(): ?DateTimeImmutable
{
return $this->email->getDate();
}
public function setDate(DateTimeInterface $date): self
{
$this->email->date($date);
return $this;
}
public function getPriority(): int
{
return $this->email->getPriority();
}
public function setPriority(int $priority): self
{
$this->email->priority($priority);
return $this;
}
public function getReturnPath(): string
{
$returnPath = $this->email->getReturnPath();
return $returnPath === null ? '' : $returnPath->getAddress();
}
public function setReturnPath(string $address): self
{
$this->email->returnPath($address);
return $this;
}
public function getSender(): string
{
$sender = $this->email->getSender();
return $sender === null ? '' : $sender->getAddress();
}
public function setSender(string $address): self
{
$this->email->sender($address);
return $this;
}
public function getTextBody(): string
{
return (string) $this->email->getTextBody();
}
public function setTextBody($text): self
{
$this->email->text($text, $this->charset);
return $this;
}
public function getHtmlBody(): string
{
return (string) $this->email->getHtmlBody();
}
public function setHtmlBody($html): self
{
$this->email->html($html, $this->charset);
return $this;
}
/**
* @inheritdoc
*/
public function attach($fileName, array $options = [])
{
$file = [];
if (!empty($options['fileName'])) {
$file['name'] = $options['fileName'];
} else {
$file['name'] = $fileName;
}
if (!empty($options['contentType'])) {
$file['contentType'] = $options['contentType'];
} else {
$file['contentType'] = mime_content_type($fileName);
}
$this->email->attachFromPath($fileName, $file['name'], $file['contentType']);
return $this;
}
/**
* @inheritdoc
*/
public function attachContent($content, array $options = [])
{
$file = [];
if (!empty($options['fileName'])) {
$file['name'] = $options['fileName'];
} else {
$file['name'] = null;
}
if (!empty($options['contentType'])) {
$file['contentType'] = $options['contentType'];
} else {
$file['contentType'] = null;
}
$this->email->attach($content, $file['name'], $file['contentType']);
return $this;
}
/**
* @inheritdoc
*/
public function embed($fileName, array $options = [])
{
$file = [];
if (!empty($options['fileName'])) {
$file['name'] = $options['fileName'];
} else {
$file['name'] = $fileName;
}
if (!empty($options['contentType'])) {
$file['contentType'] = $options['contentType'];
} else {
$file['contentType'] = mime_content_type($fileName);
}
$this->email->embedFromPath($fileName, $file['name'], $file['contentType']);
return 'cid:' . $file['name'];
}
/**
* @inheritdoc
*/
public function embedContent($content, array $options = [])
{
$file = [];
if (!empty($options['fileName'])) {
$file['name'] = $options['fileName'];
} else {
$file['name'] = null;
}
if (!empty($options['contentType'])) {
$file['contentType'] = $options['contentType'];
} else {
$file['contentType'] = null;
}
$this->email->embed($content, $file['name'], $file['contentType']);
return 'cid:' . $file['name'];
}
public function getHeader($name): array
{
$headers = $this->email->getHeaders();
if (!$headers->has($name)) {
return [];
}
$values = [];
/** @var HeaderInterface $header */
foreach ($headers->all($name) as $header) {
$values[] = $header->getBodyAsString();
}
return $values;
}
public function addHeader($name, $value): self
{
$this->email->getHeaders()->addTextHeader($name, $value);
return $this;
}
public function setHeader($name, $value): self
{
$headers = $this->email->getHeaders();
if ($headers->has($name)) {
$headers->remove($name);
}
foreach ((array) $value as $v) {
$headers->addTextHeader($name, $v);
}
return $this;
}
public function setHeaders($headers): self
{
foreach ($headers as $name => $value) {
$this->setHeader($name, $value);
}
return $this;
}
public function toString(): string
{
return $this->email->toString();
}
/**
* Returns a Symfony email instance.
*
* @return Email Symfony email instance.
*/
public function getSymfonyEmail(): Email
{
return $this->email;
}
/**
* Converts address instances to their string representations.
*
* @param Address[] $addresses
*
* @return array<string, string>|string
*/
private function convertAddressesToStrings(array $addresses)
{
$strings = [];
foreach ($addresses as $address) {
$strings[$address->getAddress()] = $address->getName();
}
return empty($strings) ? '' : $strings;
}
/**
* Converts string representations of address to their instances.
*
* @param array<int|string, string>|string $strings
*
* @return Address[]
*/
private function convertStringsToAddresses($strings): array
{
if (is_string($strings)) {
return [new Address($strings)];
}
$addresses = [];
foreach ($strings as $address => $name) {
if (!is_string($address)) {
// email address without name
$addresses[] = new Address($name);
continue;
}
$addresses[] = new Address($address, $name);
}
return $addresses;
}
}