PageRenderTime 67ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/phmte_0.3.0/phmte.php

https://github.com/lslucas/105fm
PHP | 403 lines | 220 code | 71 blank | 112 comment | 34 complexity | e692e7ae6157214d85fda44bfec2c2b1 MD5 | raw file
  1. <?php
  2. /**
  3. * Mail template class
  4. *
  5. * @author: Mario Trojan
  6. * @lastchange: 2011-09-04
  7. */
  8. class HtmlMailTemplate {
  9. protected $_tpl_id; // Basename of template path, will be used in exceptions
  10. protected $_tpl_path; // Template path from constructor
  11. protected $_tpl_info; // Template info class (set in FillTemplate)
  12. protected $_mail; // HtmlMail class (set in FillTemplate, get by GetMail)
  13. /**
  14. * Construct mail template class for an existing file
  15. *
  16. * @author: Mario Trojan
  17. * @lastchange: 2011-09-03
  18. *
  19. * @param string $path Path to a template file
  20. */
  21. public function __construct ($path) {
  22. assert('is_file($path)');
  23. $this->_tpl_path = $path;
  24. $this->_tpl_id = basename($path);
  25. }
  26. /**
  27. * Load and fill template with external informations
  28. *
  29. * - Uses DOM to load HTML file
  30. * - Creates a protected Mail-Object from the template
  31. *
  32. * @author: Mario Trojan
  33. * @lastchange: 2011-09-04
  34. *
  35. * @param HtmlMailTemplateInformation $info External informations
  36. */
  37. public function FillTemplate (HtmlMailTemplateInformation $info) {
  38. $this->_tpl_info = $info;
  39. $dom = new DOMDocument();
  40. if (!@$dom->loadHTMLFile($this->_tpl_path)) {
  41. throw new HtmlMailException("HTML-Teplate ".basename($this->_tpl_id)." could not be loaded");
  42. }
  43. $mail = new HtmlMail();
  44. $mail->charset = $info->charset;
  45. $mail->content_type = $info->content_type;
  46. $mail->attachments = $info->attachments;
  47. $mail->additional_headers = $info->additional_headers;
  48. // Substitute Variables
  49. $vars = $dom->getElementsByTagName($this->_tpl_info->mailvar_tagname);
  50. // Cache found vars because we want to replace them (avoid DOMNodeList iterator problem)
  51. $vars2 = array();
  52. foreach ($vars as $var) {
  53. $vars2[] = $var;
  54. }
  55. // Replace vars
  56. foreach ($vars2 as $var) {
  57. $key = $var->getAttribute($this->_tpl_info->mailvar_attrname_key);
  58. $type = $var->getAttribute($this->_tpl_info->mailvar_attrname_type);
  59. if ($type == '') {
  60. $type = 'string';
  61. }
  62. if (!isset($this->_tpl_info->vars[$key])) {
  63. throw new HtmlMailException("Unknown var '" . $key . "' in " . $this->_tpl_id);
  64. }
  65. switch ($type) {
  66. case 'string':
  67. // simply replace placeholder with string
  68. $text = $dom->createTextNode($this->_tpl_info->vars[$key]);
  69. $var = $var->parentNode->replaceChild($text, $var);
  70. break;
  71. case 'array':
  72. // Remove + Cache all children
  73. $to_clone = array();
  74. $child = $var->firstChild;
  75. while ($child != null) {
  76. $next = $child->nextSibling;
  77. $to_clone[] = $child->parentNode->removeChild($child);
  78. $child = $next;
  79. }
  80. // Duplicate all children once for each array entry
  81. foreach ($this->_tpl_info->vars[$key] as $entry) {
  82. $entry_index = 0;
  83. foreach ($to_clone as $node) {
  84. // If node is mailval -> replace
  85. if ($node instanceof DOMElement && $node->tagName == $info->mailval_tagname) {
  86. // Check if external data is string or array and use correct entry
  87. $entry_text = $this->FillTemplateSubGetEntryText($entry, $entry_index, $node->getAttribute($info->mailval_attrname_index));
  88. $node = $dom->createTextNode($entry_text);
  89. $entry_index++;
  90. }
  91. // if node is other element, then clone and check if children contain mailval and replace
  92. elseif ($node instanceof DOMElement) {
  93. $node = $node->cloneNode(true);
  94. $mailvals = $node->getElementsByTagName($info->mailval_tagname);
  95. // And caching again, stupid dom iterator ...
  96. $mailvals2 = array();
  97. foreach ($mailvals as $mailval) {
  98. $mailvals2[] = $mailval;
  99. }
  100. foreach ($mailvals2 as $mailval) {
  101. // Check if external data is string or array and use correct entry
  102. $entry_text = $this->FillTemplateSubGetEntryText($entry, $entry_index, $mailval->getAttribute($info->mailval_attrname_index));
  103. $text = $dom->createTextNode($entry_text);
  104. $mailval = $mailval->parentNode->replaceChild($text, $mailval);
  105. $entry_index++;
  106. }
  107. }
  108. else {
  109. $node = $node->cloneNode(true);
  110. }
  111. $var->appendChild($node);
  112. }
  113. }
  114. break;
  115. default:
  116. throw new HtmlMailException("Unknown var type '" . $type . "' in " . $this->_tpl_id);
  117. }
  118. }
  119. // Save filled template for debug purposes
  120. $mail->tpl_filled = $dom->saveHTML();
  121. // Parse fields
  122. $mailfields_tagname = $info->mailfields_tagname;
  123. $mailfields = $dom->getElementsByTagName($mailfields_tagname);
  124. if ($mailfields->length !== 1) {
  125. throw new HtmlMailException('Tag "'.$mailfields_tagname.'" is missing in ' . $this->_tpl_id);
  126. }
  127. $field_container = $mailfields->item(0);
  128. $fields = $field_container->getElementsByTagName('*');
  129. foreach ($fields as $field) {
  130. $field_name = $field->tagName;
  131. $field_value = trim($field->nodeValue);
  132. if ($field_value != '') {
  133. switch ($field_name) {
  134. case 'subject':
  135. case 'from':
  136. case 'reply_to':
  137. $mail->$field_name = $field_value;
  138. break;
  139. case 'attachment':
  140. $field_name = 'attachments';
  141. case 'to':
  142. case 'cc':
  143. case 'bcc':
  144. case 'additional_header':
  145. if (!isset($mail->$field_name)) $mail->$field_name = array();
  146. $mail->{$field_name}[] = $field_value;
  147. break;
  148. }
  149. }
  150. }
  151. // Parse message
  152. // Since DOMNode-Parameter for DOMDocument->saveHTML will be introduced in PHP 5.3.6, we need something else
  153. // Use preg_match to get mailbody from cached template
  154. $hits = array();
  155. $mailbody_tagname = $info->mailbody_tagname;
  156. if (!preg_match('"<'.$mailbody_tagname.'(\s[^>]*)?>(.*)</'.$mailbody_tagname.'>"s', $mail->tpl_filled, $hits)) {
  157. throw new HtmlMailException($mailbody_tagname . ' could not be parsed from template ' . $this->_tpl_id);
  158. }
  159. $mail->message = $hits[2];
  160. // Save mail in class variable
  161. $this->_mail = $mail;
  162. }
  163. /**
  164. * Association of variables to placeholders ("mailval") dependant on the template structure and array structure
  165. *
  166. * HtmlMailTemplateInformation->vars may for each key contain either
  167. * a string
  168. * an array of string
  169. * an array of arrays (numeric)
  170. * an array of arrays (associative)
  171. *
  172. * This function decides which entry to take dependant on previous entries in the same dataset
  173. * or an explicit key which is set as an attribute in the mailval tag.
  174. *
  175. * @author: Mario Trojan
  176. * @lastchange: 2011-09-04
  177. *
  178. * @param HtmlMailTemplateInformation $info External informations
  179. */
  180. protected function FillTemplateSubGetEntryText ($entry, $entry_index, $entry_key=null) {
  181. if (!is_array($entry)) {
  182. $entry_text = $entry;
  183. }
  184. else {
  185. if ($entry_key == null) {
  186. $keys = array_keys($entry);
  187. $entry_key = $keys[$entry_index];
  188. }
  189. if (!isset($entry[$entry_key])) {
  190. throw new HtmlMailException($entry_key . ' is not set in external data for template ' . $this->_tpl_id);
  191. }
  192. $entry_text = $entry[$entry_key];
  193. }
  194. return $entry_text;
  195. }
  196. /**
  197. * Getter for prepared mail
  198. *
  199. * @author: Mario Trojan
  200. * @lastchange: 2011-09-03
  201. *
  202. * @return HtmlMail
  203. */
  204. public function GetMail () {
  205. return $this->_mail;
  206. }
  207. /**
  208. * Send Mail after generation
  209. *
  210. * @author: Mario Trojan
  211. * @lastchange: 2011-09-03
  212. *
  213. */
  214. public function SendMail () {
  215. $this->_mail->Send();
  216. }
  217. }
  218. /**
  219. * Data class with external informations to convert template to mail
  220. *
  221. * @author: Mario Trojan
  222. * @lastchange: 2011-09-04
  223. *
  224. */
  225. class HtmlMailTemplateInformation {
  226. public $vars = array();
  227. public $attachments = array();
  228. public $additional_headers = array();
  229. public $mailvar_tagname = 'mailvar';
  230. public $mailvar_attrname_key = 'key';
  231. public $mailvar_attrname_type = 'type';
  232. public $mailval_tagname = 'mailval';
  233. public $mailval_attrname_index = 'index';
  234. public $mailfields_tagname = 'mailfields';
  235. public $mailbody_tagname = 'mailbody';
  236. public $charset = 'utf8';
  237. public $content_type = 'text/html';
  238. }
  239. /**
  240. * Class with created mail
  241. *
  242. * Can be used to send the mail
  243. *
  244. * @author: Mario Trojan
  245. * @lastchange: 2011-09-04
  246. *
  247. */
  248. class HtmlMail {
  249. public $tpl_filled; // Filled template as DOM->saveHTML, may contain additional unneccesary information (Doctype declaration, ...)
  250. public $subject; // Mail subject
  251. public $from; // Mail from (string)
  252. public $to = array(); // Mail to (array)
  253. public $cc = array(); // Mail cc (array)
  254. public $bcc = array(); // Mail bcc (array)
  255. public $reply_to; // Mail reply-to (string)
  256. public $additional_headers = array(); // Mail additional headers (array)
  257. public $attachments = array(); // Paths to Attachments
  258. public $charset = 'utf8'; // Mail charset
  259. public $content_type = 'text/html'; // Mail content-type
  260. public $message; // Html mail body from template
  261. /**
  262. * Send this mail
  263. *
  264. * @author: Mario Trojan
  265. * @lastchange: 2011-09-04
  266. *
  267. */
  268. public function Send () {
  269. $to = implode(',', $this->to);
  270. $cc = implode(',', $this->cc);
  271. $bcc = implode(',', $this->bcc);
  272. $boundary = md5(date('r', time()));
  273. $boundary_line = '--' . $boundary;
  274. $headers = '';
  275. if ($this->from != '') {
  276. $headers .= "From: " . $this->from . PHP_EOL;
  277. }
  278. if ($cc != '') {
  279. $headers .= "Cc: " . $cc . PHP_EOL;
  280. }
  281. if ($bcc != '') {
  282. $headers .= "Bcc: " . $bcc . PHP_EOL;
  283. }
  284. if ($this->reply_to != '') {
  285. $headers .= "Reply-to: " . $this->reply_to . PHP_EOL;
  286. }
  287. // Additional headers
  288. foreach ($this->additional_headers as $key => $value) {
  289. if (is_string($key)) {
  290. $headers .= $key . ': ' . $value . PHP_EOL;
  291. }
  292. else {
  293. $headers .= $value . PHP_EOL;
  294. }
  295. }
  296. $headers .= "MIME-Version: 1.0" . PHP_EOL;
  297. $headers .= "Content-Type: multipart/mixed; boundary=\"".$boundary."\"" . PHP_EOL;
  298. $message = '';
  299. // Include HTML Message
  300. $message .= $boundary_line . PHP_EOL;
  301. $message .= "Content-type: ".$this->content_type."; charset=" . $this->charset . PHP_EOL;
  302. $message .= "Content-Transfer-Encoding: base64" . PHP_EOL . PHP_EOL;
  303. $message .= chunk_split(base64_encode($this->message)) . PHP_EOL . PHP_EOL;
  304. // Include Attachments
  305. foreach ($this->attachments as $attachment) {
  306. // Prepare attachment
  307. $contents = file_get_contents($attachment);
  308. if (!$contents) throw new HtmlMailException(basename($attachment) . " could not be loaded");
  309. $attachment2 = chunk_split(base64_encode($contents));
  310. // Write attachment to mail
  311. $message .= $boundary_line . PHP_EOL;
  312. $message .= "Content-type: application/octet-stream; name=" . basename($attachment) . PHP_EOL;
  313. $message .= "Content-Transfer-Encoding: base64" . PHP_EOL;
  314. $message .= "Content-Disposition: attachment" . PHP_EOL . PHP_EOL;
  315. $message .= $attachment2 . PHP_EOL . PHP_EOL;
  316. }
  317. // Debug
  318. #print $message;
  319. // Send mail
  320. if (!mail($to, $this->subject, $message, $headers)) {
  321. throw new HtmlMailException ("Mail '" . $subject . "' could not be sent to " . $to);
  322. }
  323. }
  324. }
  325. /**
  326. * This exception will be thrown if an error occurs
  327. *
  328. * @author: Mario Trojan
  329. * @lastchange: 2011-09-03
  330. *
  331. */
  332. class HtmlMailException extends Exception {}