Helper Email


1Presentación

Helper para el envío de correos electrónicos, con la opción de enviar mensajes simples en texto plano, o mensajes más complejos (formato HTML y texto, adjuntos).

El objeto \Temma\Utils\Email ofrece dos familias de métodos.

Los métodos estáticos (simpleMail(), fullMail()) permiten enviar un correo rápidamente. Dependen de la función nativa mail() de PHP: por eso requieren un servidor SMTP local (un MTA como sendmail, Postfix o Exim) instalado y configurado en la máquina.

Los métodos orientados a objetos (textMail(), mimeMail(), templatedMail()) se usan a través del componente de inyección de dependencias y aprovechan los parámetros configurados en el archivo etc/temma.php: elegir un relay SMTP, desactivar los envíos, filtrar los dominios permitidos, añadir destinatarios en copia, y demás. Si el transporte no está configurado, también usan la función mail().

Se recomienda encarecidamente usar los métodos orientados a objetos (en lugar de los métodos estáticos), ya que esto permite configurar los envíos. Por ejemplo, puedes desactivar los envíos cuando despliegues tu sitio en un servidor de pruebas; o restringir los envíos a determinados dominios.

2Métodos estáticos

Estos métodos envían mensajes a través de la función nativa mail() de PHP: por eso requieren un servidor SMTP local (sendmail, Postfix, Exim…) instalado y configurado en la máquina. Al no tener estado de instancia, no tienen en cuenta ni la configuración x-email ni un relay; para eso, usa los métodos orientados a objetos.

2.1simpleMail()

Este método estático facilita el envío de un correo con contenido en texto plano.

Firma del método:

simpleMail(string $from, string|array $to, string $title='',
           string $message='', string|array $cc='',
           string|array $bcc='', ?string $envelopeSender=null) : void

Parámetros:

  • $from: Remitente del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $to: Destinatario del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $title: Título del mensaje.
  • $message: Contenido textual del mensaje.
  • $cc: Destinatario en copia del mensaje, o lista de destinatarios.
  • $bcc: Destinatario en copia oculta, o lista de destinatarios.
  • $envelopeSender: Dirección del remitente enviada a sendmail.

Ejemplo:

use \Temma\Utils\Email as TµEmail;

// envía un mensaje en texto plano
TµEmail::simpleMail('luke@rebellion.org', 'vader@empire.com',
                    "No puedo creerlo", "¿Eres mi padre?");

2.2fullMail()

Este método estático puede usarse para enviar correos en texto plano y/o HTML, con adjuntos si es necesario.

Firma del método:

fullMail(string $from, string|array $to, string $title='',
         string $html='', ?string $text=null, ?array $attachments=null,
         string|array $cc='', string|array $bcc='',
         ?string $unsubscribe=null, ?string $envelopeSender=null) : void

Parámetros:

  • $from: Remitente del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $to: Destinatario del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $title: Título del mensaje.
  • $html: Contenido del mensaje en formato HTML.
  • $text: Contenido textual del mensaje.
  • $attachments: Lista de adjuntos, cada uno representado por un array asociativo que contiene las siguientes claves:
    • filename: Nombre del archivo.
    • mimetype: Tipo MIME del archivo.
    • data: Contenido binario del archivo.
  • $cc: Destinatario en copia del mensaje, o lista de destinatarios.
  • $bcc: Destinatario en copia oculta, o lista de destinatarios.
  • $unsubscribe: Contenido de la cabecera SMTP List-Unsubscribe.
  • $envelopeSender: Dirección del remitente enviada a sendmail.

Ejemplo:

use \Temma\Utils\Email as TµEmail;

TµEmail::fullMail('vader@empire.com', "luke@rebellion.org",
                  "Hola, chico", "<h1>Sí</h1><p>Soy tu padre</p>");

3Métodos orientados a objetos

Estos métodos requieren que el objeto se instancie a través del componente de inyección de dependencias.

A diferencia de los métodos estáticos, su comportamiento se puede ajustar con precisión: elegir un relay SMTP (en lugar de la función local mail()), desactivar los envíos, filtrar los dominios permitidos, añadir destinatarios en copia, y demás. Esto se configura mediante el archivo de configuración y los métodos de configuración (ver más abajo).


3.1textMail()

Este método puede usarse para enviar mensajes simples en texto plano.

Firma del método:

textMail(string $from, string|array $to, string $title='',
         string $message='', string|array $cc='',
         string|array $bcc='', ?string $envelopeSender=null) : void

Parámetros:

  • $from: Remitente del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $to: Destinatario del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $title: Título del mensaje.
  • $message: Contenido textual del mensaje.
  • $cc: Destinatario en copia del mensaje, o lista de destinatarios.
  • $bcc: Destinatario en copia oculta, o lista de destinatarios.
  • $envelopeSender: Dirección del remitente enviada a sendmail.

Ejemplo:

// envía un mensaje en texto plano
$from = 'luke@rebellion.org';
$to = 'vader@empire.com';
$title = "No puedo creerlo";
$text = "¿Eres mi padre?";
$this->_loader['\Temma\Utils\Email']->textMail($from, $to, $title, $text);

3.2mimeMail()

Este método puede usarse para enviar correos en texto plano y/o HTML, con adjuntos si es necesario.

Firma del método:

mimeMail(string $from, string|array $to, string $title='',
         string $html='', ?string $text=null, ?array $attachments=null,
         string|array $cc='', string|array $bcc='',
         ?string $unsubscribe=null, ?string $envelopeSender=null) : void

Parámetros:

  • $from: Remitente del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $to: Destinatario del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $title: Título del mensaje.
  • $html: Contenido del mensaje en formato HTML.
  • $text: Contenido textual del mensaje.
  • $attachments: Lista de adjuntos, cada uno representado por un array asociativo que contiene las siguientes claves:
    • filename: Nombre del archivo.
    • mimetype: Tipo MIME del archivo.
    • data: Contenido binario del archivo.
  • $cc: Destinatario en copia del mensaje, o lista de destinatarios.
  • $bcc: Destinatario en copia oculta, o lista de destinatarios.
  • $unsubscribe: Contenido de la cabecera SMTP List-Unsubscribe.
  • $envelopeSender: Dirección del remitente enviada a sendmail.

Ejemplo:

// remitente, destinatario y título del mensaje
$from = 'vader@empire.com';
$to = 'luke@rebellion.org';
$title = 'Hola, chico';
// versiones HTML y texto plano del mensaje
$html = "<h1>Sí</h1><p>Soy tu padre</p>";
$text = "Sí. Soy tu padre";
// archivo adjunto
$attachments = [
    [
        'filename' => 'come_to_the_dark_side.pdf',
        'mimetype' => 'application/pdf',
        'data'     => file_read_contents('registration_form.pdf'),
    ],
];
// instanciación del objeto
$email = $this->_loader['\Temma\Utils\Email'];
// envía el mensaje
$email->mimeMail($from, $to, $title, $html, $text, $attachments);

3.3templatedMail()

Este método permite enviar correos en texto plano y/o HTML, posiblemente con adjuntos. Los mensajes en texto y HTML se generan a partir de templates Smarty y de los datos pasados a los templates.

Firma del método:

templatedMail(string $from, string|array $to, string $title='',
              ?string $htmlTplPath=null, ?string $textTplPath=null,
              ?array $templateData=null,?array $attachments=null,
              string|array $cc='', string|array $bcc='',
              ?string $unsubscribe=null, ?string $envelopeSender=null) : void

Parámetros:

  • $from: Remitente del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $to: Destinatario del mensaje, con el formato "direccion@dominio" o "Nombre <direccion@dominio>".
  • $title: Título del mensaje.
  • $htmlTplPath: Ruta al template HTML.
    Puede ser una ruta absoluta o una ruta relativa al directorio templates/ del proyecto.
  • $textTplPath: Ruta al template en formato texto.
    Puede ser una ruta absoluta o una ruta relativa al directorio templates/ del proyecto.
  • $templateData: Array asociativo que contiene los datos que se transmitirán a los templates.
  • $attachments: Lista de adjuntos, cada uno representado por un array asociativo que contiene las siguientes claves:
    • filename: Nombre del archivo.
    • mimetype: Tipo MIME del archivo.
    • data: Contenido binario del archivo.
  • $cc: Destinatario en copia del mensaje, o lista de destinatarios.
  • $bcc: Destinatario en copia oculta, o lista de destinatarios.
  • $unsubscribe: Contenido de la cabecera SMTP List-Unsubscribe.
  • $envelopeSender: Dirección del remitente enviada a sendmail.

Ejemplo:

// remitente, destinatario y título del mensaje
$from = 'vader@empire.com';
$to = 'luke@rebellion.org';
$title = 'Hola, chico';
// ruta a los templates HTML y texto plano
$htmlTpl = 'emails/message_pour_luke/html.tpl';
$textTpl = 'emails/message_pour_luke/text.tpl';
// datos del template
$data = [
    'name'   => 'Luky',
    'weapon' => 'Lightsaber',
];
// archivo adjunto
$attachments = [
    [
        'filename' => 'come_to_the_dark_side.pdf',
        'mimetype' => 'application/pdf',
        'data'     => file_read_contents('registration_form.pdf'),
    ],
];
// instanciación del objeto
$email = $this->_loader['\Temma\Utils\Email'];
// envía el mensaje
$email->templatedMail($from, $to, $title, $htmlTpl, $textTpl, $data, $attachments);
Escape de HTML. El template HTML se procesa con autoescape, según la configuración de la sección x-smarty (clave autoEscape, habilitado por defecto). El template de texto, en cambio, nunca pasa por autoescape (las entidades HTML en el cuerpo de un mensaje en texto plano serían un error).

3.4Archivo de configuración

Los métodos textMail(), mimeMail() y templatedMail() se ven afectados por valores que pueden definirse en el archivo etc/temma.php. Esto permite, por ejemplo, forzar la copia de destinatarios en todos los mensajes enviados.

<?php

return [
    'x-email' => [
        'disabled'       => true,
        'allowedDomains' => [ 'temma.net', 'temma.org' ],
        'cc'             => 'leia@rebellion.org',
        'bcc'            => [
            'palpatine@empire.com',
            'yoda@jedi.org',
        ],
        'envelopeSender' => 'administrator@blackstar.com',
    ]
];
  • Línea 5: Al añadir el parámetro disabled y ponerlo en true, desactivas por completo el envío de correos.
  • Línea 6: Lista de dominios a los que se pueden enviar mensajes.
  • Línea 7: Destinatario añadido en copia. Esta variable puede recibir una cadena de caracteres que contenga un destinatario (con el formato "direccion@dominio.com" o "Nombre <direccion@dominio.com>"), o una lista de destinatarios.
  • Línea 8: Destinatarios añadidos en copia oculta. Esta variable puede recibir una cadena que contenga un destinatario (con el formato "direccion@dominio.com" o "Nombre <direccion@dominio.com>"), o una lista de destinatarios.
  • Línea 12: Definición de la dirección usada como "envelope-sender" (también llamado "return path") en el sobre SMTP (comando MAIL FROM). Esto puede ser necesario en el caso de relays SMTP.

3.5Métodos de configuración

Se pueden usar métodos para modificar el comportamiento de los métodos textMail(), mimeMail() y templatedMail(). Estos métodos sobrescriben cualquier valor definido en el archivo de configuración.

$email = $this->_loader['\Temma\Utils\Email'];

// desactiva todos los envíos
$email->enable(false);
// reactiva
$email->enable(true);

// define los nombres de dominio autorizados
$email->setAllowedDomains(['temma.net', 'github.com']);

// añade un destinatario en copia
$email->setCc('contact@monsupersiteweb.com');
// añade varios destinatarios en copia
$email->setCc(['aa@bb.com', 'yy@zz.com']);

// añade un destinatario en copia oculta
$email->setBcc('aa@bb.com');
// añade varios destinatarios en copia oculta
$email->setBcc(['aa@bb.com', 'yy@zz.com']);

// define el remitente del sobre SMTP ("envelope-sender", comando MAIL FROM)
$email->setEnvelopeSender('administrator@blackstar.com');

// define el transporte de envío (ver "Envío a través de un relay SMTP")
$email->setTransport($this->_loader->dataSources['mail']);
$email->setTransport('smtp+tls://user:pass@smtp.example.com:587');

4Envío a través de un relay SMTP

4.1Principio

Por defecto, el helper Email envía los mensajes a través de la función mail() de PHP, es decir, a través del servidor de correo local (sendmail/Postfix/Exim). Este comportamiento no cambia: si no configuras ningún transporte, nada cambia para las aplicaciones existentes.

En su lugar, puedes configurar un transporte, que se encarga de la entrega del mensaje. El transporte puede ser cualquier fuente de datos que soporte el método set(): la fuente de datos SMTP entrega el mensaje mediante un relay; una fuente File o S3 lo archiva; una fuente de cola de mensajes (SQS, Beanstalk, Redis) lo encola; una fuente Dummy lo descarta (útil en entornos de prueba).

Solo los métodos orientados a objetos (textMail(), mimeMail(), templatedMail()) pueden usar un transporte. Los métodos estáticos simpleMail() y fullMail() no tienen estado de instancia: siempre pasan por mail().

4.2Configuración del transporte

Hay dos formas de designar el transporte, en orden de prioridad:

  1. Por método, en tiempo de ejecución (tiene prioridad sobre la configuración): el método setTransport() acepta una fuente de datos ya instanciada, un DSN (cualquier esquema), o un array de parámetros SMTP.
    $email = $this->_loader['\Temma\Utils\Email'];
    
    // fuente de datos existente (declarada en la sección "datasources")
    $email->setTransport($this->_loader->dataSources['mail']);
    
    // transporte al vuelo como DSN
    $email->setTransport('smtp+tls://user:pass@smtp.example.com:587');
    
    // o como parámetros SMTP separados
    $email->setTransport(['host' => 'smtp.example.com', 'port' => 587, 'security' => 'starttls',
                         'user' => 'user@example.com', 'password' => 'p@ss:w/rd!']);
    
  2. Por configuración: la directiva transport de la sección x-email. Acepta tres formas: el nombre de una fuente de datos declarada en la sección datasources, un DSN (cualquier esquema), o un array de parámetros SMTP. Temma construye entonces la fuente de datos por detrás.
    // nombre de una fuente de datos declarada en la sección "datasources"
    'datasources' => [
        'mail' => 'smtp+tls://user:pass@smtp.gmail.com:587',
    ],
    'x-email' => [
        'transport' => 'mail',
    ],
    
    // o un DSN directamente
    'x-email' => [
        'transport' => 'smtp+tls://user:pass@smtp.gmail.com:587',
    ],
    
    // o parámetros SMTP separados (útil cuando la contraseña contiene caracteres especiales)
    'x-email' => [
        'transport' => [
            'host'     => 'localhost',
            'port'     => 25,
            'security' => 'none',
        ],
    ],
    

Una cadena que contenga "://" se trata como un DSN; cualquier otra cadena se trata como el nombre de una fuente de datos declarada.

En resumen: método en tiempo de ejecución > configuración. Si no hay nada configurado, el helper usa mail().


4.3Qué se envía

Cuando se configura un transporte, los métodos orientados a objetos construyen el mensaje y luego realizan una única llamada set() sobre el transporte, con una estructura que describe el sobre y el mensaje:

  • from: remitente del sobre (el valor de envelopeSender si está definido, si no la dirección From), usado para MAIL FROM;
  • recipients: unión sin duplicados de los destinatarios to + cc + bcc, usada para el RCPT TO;
  • message: el mensaje completo en formato RFC 5322 (con Date y Message-ID), sin cabecera Bcc (los destinatarios en copia oculta están en el sobre, no en el mensaje).

Esto es lo que hace portátil el transporte: el sobre viaja en el valor pasado a set(), nunca en la clave ni en las opciones. Una fuente de datos de almacenamiento, por tanto, archiva el mensaje sin perder nada. Consulta la página de la fuente de datos SMTP para más detalles.


4.4Portabilidad y casos límite

El transporte está tipado como \Temma\Base\Datasource (no específicamente SMTP), porque el helper solo llama al método genérico set(). Una fuente de datos que no soporte set() (por ejemplo las fuentes AI/OpenAI) lanzaría una excepción \Temma\Exceptions\Database; este caso es marginal y no se gestiona de forma específica.

Aspectos operativos (sin código). La capacidad de entrega depende principalmente de la configuración DNS y de la elección del relay:
  • SPF / DMARC: registros DNS + un relay autorizado. La única palanca a nivel de aplicación es el control del remitente del sobre (envelopeSender) y del From, ya disponible.
  • DKIM: la firma suele estar a cargo del relay.