* @license PHP License * @package Wombat * @subpackage base */ /** * Send emails * * Simple interface to send emails * * @version 0.2.0 * @package Wombat * @subpackage base */ class WBMailer extends WBStdClass { protected $header = array( 'From' => 'test@example.com', 'X-Mailer' => 'Wombat Framework Mailer', 'MIME-Version' => '1.0', 'Content-Type' => 'text/plain; charset="utf-8"' ); /** * mailer backend * @var Mail */ protected $backend; /** * template engine * @var patTemplate */ protected $tmpl; /** * config loader * @var WBConfig */ protected $config; /** * list of recipients * @var array */ protected $rcpt = array(); /** * additional data to fill email templates * @var array */ protected $data = array(); const ADDR_TO = 1; const ADDR_CC = 2; /** * list of addresses to be added to email-header * @var array */ protected $address = array( self::ADDR_TO => array(), self::ADDR_CC => array() ); /** * constructor * * Load header config and start template engine */ public function __construct() { $this->config = WBClass::create('WBConfig'); $this->config->load('config'); // overwrite header config $header = $this->config->get('network/mail/header'); if (is_array($header)) { $this->header = array_merge($this->header, $header); } $this->tmpl = WBClass::create('patTemplate'); } /** * set template file * * Set template file that holds at least templates: * - email_subject * - email_plain * Optionally, use add template: email_html for HTML email * * Global template variables are also supported. Those templates will be used to * build email's subject as well as html and plain text body * * @param string $tmpl */ public function setTemplate($tmpl) { // start from scratch $this->tmpl->freeAllTemplates(); $result = $this->tmpl->readTemplatesFromInput('Mailer/' . $tmpl . '.tmpl'); if (patErrorManager::isError($result)) { WBClass::load('WBException_Argument'); throw new WBException_Argument('Could not load template', 1, __CLASS__); } } /** * add one or more recipient * * add list ot internal list of recipients * * @param string|array $rcpt email address(es) */ public function addRcpt($rcpt) { if (!is_array($rcpt)) { $rcpt = array($rcpt); } $this->rcpt = array_merge($this->rcpt, $rcpt); $this->rcpt = array_unique($this->rcpt); } /** * set from address * * encode address add it to From-header * * @param string $address */ public function setFrom($address) { $this->header['From'] = $this->encodeAddress($address); } /** * add address to To * * encode address and prepare to add it to email header * * @param string $address */ public function addTo($address) { $this->address[self::ADDR_TO][] = $this->encodeAddress($address); } /** * add address to CC * * encode address and prepare to add it to email header * * @see addTo() * @param string $address */ public function addCc($address) { $this->address[self::ADDR_CC][] = $this->encodeAddress($address); } /** * base64 encode email address * * See whether an email address contains name and the actual address * and encode it accordingly * * @param string $address * @return string */ protected function encodeAddress($address) { // base 64 encode if required if (strstr($address, '<')) { $address = explode('<', $address, 2); $address = '=?UTF-8?B?' . base64_encode($address[0]) . '?= <' . $address[1]; } return $address; } /** * set data to fill placeholders * * The given array will be passed to patTemplate to be added * as global variables * * @param array $data * @param string prefix */ public function setData(Array $data, $prefix = '') { $this->data = array(); $this->addData($data, $prefix); } /** * add data to fill placeholders * * @see setData() * @param array $data * @param string prefix */ public function addData(Array $data, $prefix = '') { if (empty($prefix)) { $this->data = array_merge($this->data, $data); return; } foreach ($data as $k => $v) { $this->data[$prefix . $k] = $v; } } /** * send email * * Build email body and complete email headers. If everything is OK, send email * * @todo add support for HTML mime emails and attachements * @throws WBException_Argument * @throws WBException_Pear */ public function send() { if (empty($this->rcpt)) { WBClass::load('WBException_Argument'); throw new WBException_Argument('At least on recipient is required to send an email, still the list of recipients is empty', 2, __CLASS__); } if (!$this->tmpl->exists('email_plain')) { WBClass::load('WBException_Argument'); throw new WBException_Argument('Template "email_plain" is missing, probably because the template file was not set, yet.', 3, __CLASS__); } if (!$this->tmpl->exists('email_subject')) { WBClass::load('WBException_Argument'); throw new WBException_Argument('Template "email_subject" is missing, probably because the template file was not set, yet.', 4, __CLASS__); } // mail body WBClass::load('WBString'); $this->tmpl->clearAllTemplates(); $this->addData2Template(); // plain text email body $plain = $this->tmpl->getParsedTemplate('email_plain'); $plain = WBString::replaceSuperPlaceholders($plain); // mail header $header = $this->header; // load subject from template and make sure there is only one line $subj = $this->tmpl->getParsedTemplate('email_subject'); $subj = trim($subj); $subj = explode("\n", $subj); $subj = $subj[0]; $subj = '=?UTF-8?B?' . base64_encode($subj) . '?='; $header['Subject'] = $subj; if (!empty($this->address[self::ADDR_TO])) { $header['To'] = implode(', ', $this->address[self::ADDR_TO]); $this->address[self::ADDR_TO] = array(); } if (!empty($this->address[self::ADDR_CC])) { $header['Cc'] = implode(', ', $this->address[self::ADDR_CC]); $this->address[self::ADDR_CC] = array(); } // initialize actual email backend $backend = $this->config->get('network/mail/backend', 'mail'); $param = $this->config->get('network/mail/parameter', array()); if (!is_array($param)) { $param = array(); } // configure mock backend if ('mock' == $backend) { $mock = WBClass::create('WBMail_Mock', $param); $param['preSendCallback'] = array($mock, 'preSend'); $param['postSendCallback'] = array($mock, 'postSend'); } $this->rcpt = implode(', ', $this->rcpt); WBClass::load('Mail'); // suppress error messages, because PEAR classes throw E_STRICT warnings $mailer = @Mail::factory($backend, $param); $result = $mailer->send($this->rcpt, $header, $plain); $this->rcpt = array(); if (@PEAR::isError($result)) { WBClass::load('WBException_Pear'); throw new WBException_PEAR($result->getMessage(), $result->getCode(), __CLASS__); } } /** * Set custom e-mail header * * @var string $header * @var string $value */ public function setHeader($header, $value) { $this->header[$header] = $value; } /** * Add mixed data to template * * Devide scalar variables and lists. Try to find matching templates and add list entries */ private function addData2Template() { $scalar = array(); foreach ($this->data as $k => $v) { if (!is_array($v)) { $scalar[$k] = $v; continue; } if(empty($v)) { $scalar[$k] = ''; continue; } $scalar[$k . '_count'] = count($v); if (!$this->tmpl->exists($k . '_list_entry')) { $scalar[$k] = implode(', ', $v); continue; } if (isset($v[0])) { $this->tmpl->addRows($k . '_list_entry', $v); } else { $this->tmpl->addVars($k . '_list_entry', $v); } } $this->tmpl->addGlobalVars($scalar); } }