inclure une version simplifiée de nulib/mail
This commit is contained in:
parent
ee33699608
commit
6b7d4b683d
17
.idea/php.xml
generated
17
.idea/php.xml
generated
@ -55,6 +55,23 @@
|
||||
<path value="$PROJECT_DIR$/php/vendor/myclabs/deep-copy" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/nikic/php-parser" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/composer" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/league/config" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/nette/schema" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/league/commonmark" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/dflydev/dot-access-data" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/psr/event-dispatcher" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/nette/utils" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/psr/container" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/psr/log" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/psr/cache" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/phpmailer/phpmailer" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/symfony/expression-language" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/symfony/cache" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/symfony/cache-contracts" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/symfony/polyfill-php80" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/symfony/service-contracts" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/symfony/polyfill-php73" />
|
||||
<path value="$PROJECT_DIR$/php/vendor/symfony/var-exporter" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="7.4">
|
||||
|
@ -19,6 +19,9 @@
|
||||
},
|
||||
"require": {
|
||||
"symfony/yaml": "^5.0",
|
||||
"phpmailer/phpmailer": "^6.8",
|
||||
"symfony/expression-language": "^5.4",
|
||||
"league/commonmark": "^2.4",
|
||||
"ext-json": "*",
|
||||
"php": "^7.4"
|
||||
},
|
||||
|
1377
composer.lock
generated
1377
composer.lock
generated
File diff suppressed because it is too large
Load Diff
120
php/src/mail/MailTemplate.php
Normal file
120
php/src/mail/MailTemplate.php
Normal file
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
namespace nulib\mail;
|
||||
|
||||
use nulib\cv;
|
||||
use nulib\str;
|
||||
use nulib\ValueException;
|
||||
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
|
||||
|
||||
class MailTemplate {
|
||||
const SCHEMA = [
|
||||
"subject" => "string",
|
||||
"body" => "string",
|
||||
"exprs" => "array",
|
||||
];
|
||||
|
||||
function __construct(array $mail) {
|
||||
$tsubject = $mail["subject"] ?? null;
|
||||
$tbody = $mail["body"] ?? null;
|
||||
$texprs = $mail["exprs"] ?? [];
|
||||
|
||||
$this->el = new ExpressionLanguage();
|
||||
ValueException::check_null($this->subject = $tsubject, "subject");
|
||||
ValueException::check_null($this->body = $tbody, "body");
|
||||
$exprs = [];
|
||||
# Commencer par extraire les expressions de la forme {name}
|
||||
if (preg_match_all('/\{([a-zA-Z_][a-zA-Z0-9_.-]*)}/', $this->body, $mss, PREG_SET_ORDER)) {
|
||||
foreach ($mss as $ms) {
|
||||
$key = $ms[0];
|
||||
$expr = str_replace("'", "\\'", $ms[1]);
|
||||
$expr = "_helper.value('$expr')";
|
||||
$exprs[$key] = $expr;
|
||||
}
|
||||
}
|
||||
$index = 0;
|
||||
foreach ($texprs as $key => $expr) {
|
||||
$prefix = null;
|
||||
$orig = $expr;
|
||||
if (preg_match('/^\[([^]]*)]/', $expr, $ms)) {
|
||||
# un préfixe spécifié de la forme [prefix]expr permet de reconnaitre les
|
||||
# formes spéciales de expr (+, *, .) qui sont précédées de prefix
|
||||
# exemple: [https://]+app.url permettra d'utiliser un texte markdown
|
||||
# de la forme <https://+app.url> qui est correctement reconnu comme un
|
||||
# url
|
||||
$prefix = $ms[1];
|
||||
$expr = substr($expr, strlen($ms[0]));
|
||||
}
|
||||
$mapKey = false;
|
||||
if (str::del_prefix($expr, "+")) {
|
||||
# config
|
||||
$mapKey = "$prefix+$expr";
|
||||
$expr = str_replace("'", "\\'", $expr);
|
||||
$expr = "_helper.config('$expr')";
|
||||
} elseif (str::del_prefix($expr, "*")) {
|
||||
# session
|
||||
$mapKey = "$prefix*$expr";
|
||||
$expr = str_replace("'", "\\'", $expr);
|
||||
$expr = "_helper.session('$expr')";
|
||||
} elseif (str::del_prefix($expr, ".")) {
|
||||
# session
|
||||
$mapKey = "$prefix.$expr";
|
||||
$expr = str_replace("'", "\\'", $expr);
|
||||
$expr = "_helper.value('$expr')";
|
||||
} elseif ($prefix !== null) {
|
||||
# sinon remettre le préfixe
|
||||
$expr = $orig;
|
||||
}
|
||||
|
||||
if ($key === $index) {
|
||||
$index++;
|
||||
if ($mapKey !== false) {
|
||||
$exprs[$mapKey] = $expr;
|
||||
} else {
|
||||
# clé normale: la correspondance est en minuscule
|
||||
$exprs[$expr] = strtolower($expr);
|
||||
}
|
||||
} else {
|
||||
$exprs[$key] = $expr;
|
||||
}
|
||||
}
|
||||
uksort($exprs, function ($a, $b) {
|
||||
return -cv::complen($a, $b);
|
||||
});
|
||||
$this->exprs = $exprs;
|
||||
}
|
||||
|
||||
/** @var ExpressionLanguage */
|
||||
protected $el;
|
||||
|
||||
protected $subject;
|
||||
|
||||
protected $body;
|
||||
|
||||
protected $exprs;
|
||||
|
||||
protected function _eval(string $template, ?array $data): string {
|
||||
if ($data === null) return $template;
|
||||
$el = $this->el;
|
||||
foreach ($this->exprs as $key => $expr) {
|
||||
$value = $el->evaluate($expr, $data);
|
||||
if (is_array($value)) $value = str::join(" ", $value);
|
||||
elseif (!is_string($value)) $value = strval($value);
|
||||
$template = str_replace($key, $value, $template);
|
||||
}
|
||||
return $template;
|
||||
}
|
||||
|
||||
function eval(?array $data, $convertMd=true): array {
|
||||
if ($data !== null) {
|
||||
$data["_helper"] = new MailTemplateHelper($data);
|
||||
}
|
||||
$subject = $this->_eval($this->subject, $data);
|
||||
$body = $this->body;
|
||||
if ($convertMd) $body = mdc::convert($body);
|
||||
$body = $this->_eval($body, $data);
|
||||
return [
|
||||
"subject" => $subject,
|
||||
"body" => $body,
|
||||
];
|
||||
}
|
||||
}
|
22
php/src/mail/MailTemplateHelper.php
Normal file
22
php/src/mail/MailTemplateHelper.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace nulib\mail;
|
||||
|
||||
use nulib\cl;
|
||||
|
||||
class MailTemplateHelper {
|
||||
function __construct(?array $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
function value(string $pkey) {
|
||||
return cl::pget($this->data, $pkey);
|
||||
}
|
||||
|
||||
function config(string $pkey) {
|
||||
return config::get($pkey);
|
||||
}
|
||||
|
||||
function session(string $pkey) {
|
||||
return session::pget($pkey);
|
||||
}
|
||||
}
|
7
php/src/mail/MailerException.php
Normal file
7
php/src/mail/MailerException.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
namespace nulib\mail;
|
||||
|
||||
use nulib\UserException;
|
||||
|
||||
class MailerException extends UserException {
|
||||
}
|
188
php/src/mail/mailer.php
Normal file
188
php/src/mail/mailer.php
Normal file
@ -0,0 +1,188 @@
|
||||
<?php
|
||||
namespace nulib\mail;
|
||||
|
||||
use nulib\cl;
|
||||
use nulib\cv;
|
||||
use nulib\output\msg;
|
||||
use nulib\str;
|
||||
use nulib\ValueException;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
|
||||
class mailer {
|
||||
private static function is_bool(&$value): bool {
|
||||
if ($value === null) {
|
||||
return false;
|
||||
} elseif (is_bool($value)) {
|
||||
return true;
|
||||
} elseif (is_int($value)) {
|
||||
$value = boolval($value);
|
||||
return true;
|
||||
} else {
|
||||
switch (strval($value)) {
|
||||
case "0":
|
||||
case "no":
|
||||
case "off":
|
||||
case "false":
|
||||
$value = false;
|
||||
return true;
|
||||
case "1":
|
||||
case "yes":
|
||||
case "on":
|
||||
case "true":
|
||||
$value = true;
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function get_bool($value): bool {
|
||||
if (self::is_bool($value)) return $value;
|
||||
else return false;
|
||||
}
|
||||
|
||||
const FROM = "no-reply@univ-reunion.fr";
|
||||
|
||||
const SCHEMA = [
|
||||
"backend" => ["string", "smtp"],
|
||||
"debug" => ["int", SMTP::DEBUG_OFF],
|
||||
"host" => ["?string", "smtp.univ.run"],
|
||||
"port" => ["?int", 25],
|
||||
"auth" => "?bool",
|
||||
"username" => "?string",
|
||||
"password" => "?string",
|
||||
"secure" => "?string",
|
||||
];
|
||||
|
||||
static function get(?array $params=null, ?bool $exceptions=null): PHPMailer {
|
||||
$mailer = new PHPMailer($exceptions);
|
||||
$mailer->setLanguage("fr");
|
||||
$mailer->CharSet = PHPMailer::CHARSET_UTF8;
|
||||
# backend
|
||||
$backend = $params["backend"] ?? null;
|
||||
$backend ??= cv::vn(getenv("NULIB_MAIL_BACKEND"));
|
||||
$backend ??= "smtp";
|
||||
switch ($backend) {
|
||||
case "smtp":
|
||||
# host
|
||||
$host = $params["host"] ?? null;
|
||||
$host ??= cv::vn(getenv("NULIB_MAIL_HOST"));
|
||||
# port
|
||||
$port = $params["port"] ?? null;
|
||||
$port ??= cv::vn(getenv("NULIB_MAIL_PORT"));
|
||||
$port ??= 25;
|
||||
if ($host === null) {
|
||||
throw new ValueException("mail host is required");
|
||||
}
|
||||
msg::debug("new PHPMailer using SMTP to $host:$port");
|
||||
$mailer->isSMTP();
|
||||
$mailer->Host = $host;
|
||||
$mailer->Port = $port;
|
||||
break;
|
||||
case "phpmail":
|
||||
msg::debug("new PHPMailer using PHPmail");
|
||||
$mailer->isMail();
|
||||
break;
|
||||
case "sendmail":
|
||||
msg::debug("new PHPMailer using sendmail");
|
||||
$mailer->isSendmail();
|
||||
break;
|
||||
default:
|
||||
throw ValueException::invalid_value($backend, "mailer backend");
|
||||
}
|
||||
# debug
|
||||
$debug = $params["debug"] ?? null;
|
||||
$debug ??= cv::vn(getenv("NULIB_MAIL_DEBUG"));
|
||||
$debug ??= SMTP::DEBUG_OFF;
|
||||
if (is_int($debug)) {
|
||||
if ($debug < SMTP::DEBUG_OFF) $debug = SMTP::DEBUG_OFF;
|
||||
elseif ($debug > SMTP::DEBUG_LOWLEVEL) $debug = SMTP::DEBUG_LOWLEVEL;
|
||||
} elseif (!self::is_bool($debug)) {
|
||||
throw ValueException::invalid_value($debug, "debug mode");
|
||||
}
|
||||
$mailer->SMTPDebug = $debug;
|
||||
# auth, username, password
|
||||
$username = $params["username"] ?? null;
|
||||
$username ??= cv::vn(getenv("NULIB_MAIL_USERNAME"));
|
||||
$password = $params["password"] ?? null;
|
||||
$password ??= cv::vn(getenv("NULIB_MAIL_PASSWORD"));
|
||||
$auth = $params["auth"] ?? null;
|
||||
$auth ??= cv::vn(getenv("NULIB_MAIL_AUTH"));
|
||||
$auth ??= $username !== null && $password !== null;
|
||||
$mailer->SMTPAuth = self::get_bool($auth);
|
||||
$mailer->Username = $username;
|
||||
$mailer->Password = $password;
|
||||
# secure
|
||||
$secure = $params["secure"] ?? null;
|
||||
$secure ??= cv::vn(getenv("NULIB_MAIL_SECURE"));
|
||||
$secure ??= false;
|
||||
if (self::is_bool($secure)) {
|
||||
if (!$secure) {
|
||||
$mailer->SMTPSecure = "";
|
||||
$mailer->SMTPAutoTLS = false;
|
||||
}
|
||||
} else {
|
||||
switch ($secure) {
|
||||
case PHPMailer::ENCRYPTION_SMTPS:
|
||||
case PHPMailer::ENCRYPTION_STARTTLS:
|
||||
$mailer->SMTPSecure = $secure;
|
||||
break;
|
||||
default:
|
||||
throw ValueException::invalid_value($secure, "encryption mode");
|
||||
}
|
||||
}
|
||||
|
||||
return $mailer;
|
||||
}
|
||||
|
||||
static function build($to, string $subject, string $body, $cc=null, $bcc=null, ?string $from=null, ?PHPMailer $mailer=null): PHPMailer {
|
||||
if ($mailer === null) $mailer = self::get();
|
||||
$mailer->clearAllRecipients();
|
||||
|
||||
if ($from === null) $from = static::FROM;
|
||||
$mailer->setFrom($from);
|
||||
foreach (cl::with($to) as $tos) {
|
||||
foreach (preg_split('/\s*[,;]\s*/', trim($tos)) as $to) {
|
||||
$mailer->addAddress($to);
|
||||
}
|
||||
}
|
||||
foreach (cl::with($cc) as $ccs) {
|
||||
foreach (preg_split('/\s*[,;]\s*/', trim($ccs)) as $cc) {
|
||||
$mailer->addCC($cc);
|
||||
}
|
||||
}
|
||||
foreach (cl::with($bcc) as $bccs) {
|
||||
foreach (preg_split('/\s*[,;]\s*/', trim($bccs)) as $bcc) {
|
||||
$mailer->addBCC($bcc);
|
||||
}
|
||||
}
|
||||
$mailer->isHTML();
|
||||
$mailer->Subject = $subject;
|
||||
$mailer->Body = $body;
|
||||
return $mailer;
|
||||
}
|
||||
|
||||
static function _send(PHPMailer $mailer): void {
|
||||
$tos = [];
|
||||
foreach ($mailer->getToAddresses() as $to) {
|
||||
$tos[] = $to[0];
|
||||
}
|
||||
$tos = str::join(",", $tos);
|
||||
msg::debug("Sending to $tos");
|
||||
if (!$mailer->send()) {
|
||||
throw new MailerException("Une erreur s'est produite pendant l'envoi du mail", $mailer->ErrorInfo);
|
||||
}
|
||||
}
|
||||
|
||||
static function send($to, string $subject, string $body, $cc=null, $bcc=null, ?string $from=null, ?PHPMailer $mailer=null): void {
|
||||
self::_send(self::build($to, $subject, $body, $cc, $bcc, $from, $mailer));
|
||||
}
|
||||
|
||||
static function tsend(array $template, array $data, $to, $cc=null, $bcc=null, ?string $from=null): void {
|
||||
$template = new MailTemplate($template);
|
||||
$mail = $template->eval($data);
|
||||
self::send($to, $mail["subject"], $mail["body"], $cc, $bcc, $from);
|
||||
}
|
||||
}
|
22
php/src/mail/mdc.php
Normal file
22
php/src/mail/mdc.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
namespace nulib\mail;
|
||||
|
||||
use League\CommonMark\GithubFlavoredMarkdownConverter;
|
||||
use League\CommonMark\MarkdownConverter;
|
||||
|
||||
class mdc {
|
||||
private static $mdc;
|
||||
|
||||
static function mdc(): MarkdownConverter {
|
||||
if (self::$mdc === null) {
|
||||
self::$mdc = new GithubFlavoredMarkdownConverter([
|
||||
"allow_unsafe_links" => false,
|
||||
]);
|
||||
}
|
||||
return self::$mdc;
|
||||
}
|
||||
|
||||
static function convert(string $text): string {
|
||||
return self::mdc()->convert($text);
|
||||
}
|
||||
}
|
26
php/tbin/mail.php
Executable file
26
php/tbin/mail.php
Executable file
@ -0,0 +1,26 @@
|
||||
#!/usr/bin/php
|
||||
<?php
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use lib\mail\mailer;
|
||||
use nur\cli\Application;
|
||||
|
||||
Application::run(new class extends Application {
|
||||
const ARGS = [
|
||||
"merge" => parent::ARGS,
|
||||
["-t", "--to", "args" => 1, "action" => "--add", "name" => "to"],
|
||||
["-c", "--cc", "args" => 1, "action" => "--add", "name" => "cc"],
|
||||
["-b", "--bcc", "args" => 1, "action" => "--add", "name" => "bcc"],
|
||||
["-F", "--from", "args" => 1, "name" => "from"],
|
||||
["args" => 2, "name" => "args"],
|
||||
];
|
||||
|
||||
protected $to, $cc, $bcc, $from;
|
||||
protected $args;
|
||||
|
||||
function main() {
|
||||
$subject = $this->args[0];
|
||||
$body = $this->args[1];
|
||||
mailer::send($this->to, $subject, $body, $this->cc, $this->bcc, $this->from);
|
||||
}
|
||||
});
|
19
php/tbin/test_mail.php
Normal file
19
php/tbin/test_mail.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
use nulib\mail\mailer;
|
||||
|
||||
putenv("NULIB_MAIL_HOST=maildev.devel.self");
|
||||
|
||||
$template = [
|
||||
"subject" => "test de mail",
|
||||
"body" => <<<EOF
|
||||
bonjour,
|
||||
|
||||
ceci est un test de mail pour {dest}
|
||||
EOF
|
||||
];
|
||||
$data = [
|
||||
"dest" => "moi même",
|
||||
];
|
||||
mailer::tsend($template, $data, "jephte.clain@gmail.com");
|
34
php/tests/mail/MailTemplateTest.php
Normal file
34
php/tests/mail/MailTemplateTest.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
namespace nulib\mail;
|
||||
|
||||
use nur\t\TestCase;
|
||||
|
||||
class MailTemplateTest extends TestCase {
|
||||
function testTemplate() {
|
||||
$mail = [
|
||||
"subject" => "infos pour NOM PRENOM",
|
||||
"body" => <<<EOT
|
||||
bonjour PRENOM NOM,
|
||||
|
||||
vous avez AGE ans
|
||||
EOT,
|
||||
"exprs" => [
|
||||
"PRENOM" => "prenom",
|
||||
"NOM" => "nom",
|
||||
"AGE" => "age",
|
||||
],
|
||||
];
|
||||
|
||||
$tpl = new MailTemplate($mail);
|
||||
[
|
||||
"subject" => $subject,
|
||||
"body" => $body,
|
||||
] = $tpl->eval([
|
||||
"nom" => "Clain",
|
||||
"prenom" => "Jephté",
|
||||
"age" => 47,
|
||||
]);
|
||||
self::assertSame("infos pour Clain Jephté", $subject);
|
||||
self::assertSame("<p>bonjour Jephté Clain,</p>\n<p>vous avez 47 ans</p>\n", $body);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user