source: trunk/lib/Email.inc.php @ 24

Last change on this file since 24 was 24, checked in by scdev, 18 years ago
File size: 11.7 KB
Line 
1<?php
2/**
3 * Email.inc.php
4 * code by strangecode :: www.strangecode.com :: this document contains copyrighted information
5 *
6 * Easy email template usage.
7 *
8 * @author  Quinn Comendant <quinn@strangecode.com>
9 * @version 1.0
10-------------------------------------------------------------------------------------
11// Example.
12$email = new Email(array(
13    'to' => array($frm['email'], 'q@lovemachine.local'),
14    'from' => sprintf('%s <%s>', App::getParam('site_name'), App::getParam('site_email')),
15    'subject' => 'Your account has been activated',
16));
17$email->setTemplate('email_registration_confirm.ihtml');
18$email->replace(array(
19    'site_name' => App::getParam('site_name'),
20    'site_url' => App::getParam('site_url'),
21    'username' => $frm['username'],
22    'password' => $frm['password1'],
23));
24if ($email->send()) {
25    App::raiseMsg(sprintf(_("A confirmation email has been sent to %s."), $frm['email']), MSG_SUCCESS, __FILE__, __LINE__);
26} else {
27    App::logMsg(sprintf('Error sending confirmation email to address %s', $frm['email']), LOG_DEBUG, __FILE__, __LINE__);
28}
29-------------------------------------------------------------------------------------
30 */
31class Email {
32
33    // Default parameters, to be overwritten by setParam() and read with getParam()
34    var $_params = array(
35        'to' => null,
36        'from' => null,
37        'subject' => null,
38        'regex' => null
39    );
40   
41    // String that contains the email body.
42    var $_template;
43   
44    // String that contains the email body after replacements.
45    var $_template_replaced;
46
47    /**
48     * Constructor.
49     *
50     * @access  public
51     * @param   array   $params     Array of object parameters.
52     * @author  Quinn Comendant <quinn@strangecode.com>
53     * @since   28 Nov 2005 12:59:41
54     */
55    function Email($params=null)
56    {
57        // The regex used in validEmail(). Set here instead of in the default _params above so we can use the concatination . dot.
58        // This matches an email address as complex as:
59        //      Bob Smith <bob&smith's/dep=sales!@smith-wick.ca.us> (Sales department)
60        // ...and something as simple as:
61        //      x@x.com
62        $this->setParam(array('regex' => '/^(?:[^,@]*\s+|[^,@]*(<)|)'   // Display name
63        . '((?:[^.<>\s@\",\[\]]+[^<>\s@\",\[\]])*[^.<>\s@\",\[\]]+)'    // Local-part
64        . '@'                                                           // @
65        . '((?:(\[)|[A-Z0-9]?)'                                         // Domain, first char
66        . '(?(4)'                                                       // Domain conditional for if first domain char is [
67        . '(?:[0-9]{1,3}\.){3}[0-9]{1,3}\]'                             // TRUE, matches IP address
68        . '|'
69        . '[.-]?(?:[A-Z0-9]+[-.])*(?:[A-Z0-9]+\.)+[A-Z]{2,6}))'         // FALSE, matches domain name
70        . '(?(1)'                                                       // Comment conditional for if initial < exists
71        . '(?:>\s*|>\s+\([^,@]+\)\s*)'                                  // TRUE, ensure ending >
72        . '|'
73        . '(?:|\s*|\s+\([^,@]+\)\s*))$/i'));                            // FALSE ensure there is no ending >
74
75        $this->setParam($params);
76    }
77
78    /**
79     * Set (or overwrite existing) parameters by passing an array of new parameters.
80     *
81     * @access public
82     * @param  array    $params     Array of parameters (key => val pairs).
83     */
84    function setParam($params)
85    {
86        if (isset($params) && is_array($params)) {
87            // Enforce valid email addresses.
88            if (isset($params['to']) && !$this->validEmail($params['to'])) {
89                $params['to'] = null;
90            }
91            if (isset($params['from']) && !$this->validEmail($params['from'])) {
92                $params['from'] = null;
93            }
94
95            // Merge new parameters with old overriding only those passed.
96            $this->_params = array_merge($this->_params, $params);
97        } else {
98            App::logMsg(sprintf('Parameters are not an array: %s', $params), LOG_ERR, __FILE__, __LINE__);
99        }
100    }
101
102    /**
103     * Return the value of a parameter, if it exists.
104     *
105     * @access public
106     * @param string $param        Which parameter to return.
107     * @return mixed               Configured parameter value.
108     */
109    function getParam($param)
110    {
111        if (isset($this->_params[$param])) {
112            return $this->_params[$param];
113        } else {
114            App::logMsg(sprintf('Parameter is not set: %s', $param), LOG_NOTICE, __FILE__, __LINE__);
115            return null;
116        }
117    }
118
119    /**
120     * Loads template from file to generate email body.
121     *
122     * @access  public
123     * @param   string  $template   Filename of email template.
124     * @author  Quinn Comendant <quinn@strangecode.com>
125     * @since   28 Nov 2005 12:56:23
126     */
127    function setTemplate($template)
128    {
129        // Load file, using include_path.
130        if (!$this->_template = file_get_contents($template, true)) {
131            App::logMsg(sprintf('Email template file does not exist: %s', $template), LOG_ERR, __FILE__, __LINE__);
132            $this->_template = null;
133            $this->_template_replaced = null;
134            return false;
135        }
136        // This could be a new template, so reset the _template_replaced.
137        $this->_template_replaced = null;
138        return true;
139    }
140
141    /**
142     * Loads template from string to generate email body.
143     *
144     * @access  public
145     * @param   string  $template   Filename of email template.
146     * @author  Quinn Comendant <quinn@strangecode.com>
147     * @since   28 Nov 2005 12:56:23
148     */
149    function setString($string)
150    {
151        // Load file, using include_path.
152        if ('' == $string) {
153            App::logMsg(sprintf('Empty string provided.', null), LOG_ERR, __FILE__, __LINE__);
154            $this->_template_replaced = null;
155            return false;
156        } else {
157            $_template = $string;
158            // This could be a new template, so reset the _template_replaced.
159            $this->_template_replaced = null;
160            return true;
161        }
162    }
163   
164    /**
165     * Replace variables in template with argument data.
166     *
167     * @access  public
168     * @param   array   $replacements   Array keys are the values to search for, array vales are the replacement values.
169     * @author  Quinn Comendant <quinn@strangecode.com>
170     * @since   28 Nov 2005 13:08:51
171     */
172    function replace($replacements)
173    {
174        // Ensure template exists.
175        if (!isset($this->_template)) {
176            App::logMsg(sprintf('Cannot replace variables, no template defined.', null), LOG_ERR, __FILE__, __LINE__);
177            return false;
178        }
179       
180        // Ensure replacements argument is an array.
181        if (!is_array($replacements)) {
182            App::logMsg(sprintf('Cannot replace variables, invalid replacements.', null), LOG_ERR, __FILE__, __LINE__);
183            return false;
184        }
185       
186        // Apply regex pattern to search elements.
187        $search = array_keys($replacements);
188        array_walk($search, create_function('&$v', '$v = "/{" . preg_quote($v) . "}/i";'));
189
190        // Replace values.
191        $replace = array_values($replacements);
192       
193        // Search and replace all values at once.
194        $this->_template_replaced = preg_replace($search, $replace, $this->_template);
195    }
196
197    /**
198     * Send email using PHP's mail() function.
199     *
200     * @access  public
201     * @param   string  $to
202     * @param   string  $from
203     * @param   string  $subject
204     * @author  Quinn Comendant <quinn@strangecode.com>
205     * @since   28 Nov 2005 12:56:09
206     */
207    function send($to=null, $from=null, $subject=null)
208    {
209        // Use arguments if provided.
210        if (isset($to)) {
211             $this->setParam(array('to' => $to));
212        }
213        if (isset($from)) {
214             $this->setParam(array('from' => $from));
215        }
216        if (isset($subject)) {
217             $this->setParam(array('subject' => $subject));
218        }
219   
220   
221        // Ensure required values exist.
222        if (!isset($this->_template)) {
223            App::logMsg(sprintf('Cannot send email. Template not set.', null), LOG_ERR, __FILE__, __LINE__);
224            return false;
225        } else if (!isset($this->_params['to'])) {
226            App::logMsg(sprintf('Cannot send email. TO not defined.', null), LOG_ERR, __FILE__, __LINE__);
227            return false;
228        } else if (!isset($this->_params['from'])) {
229            App::logMsg(sprintf('Cannot send email. FROM not defined.', null), LOG_ERR, __FILE__, __LINE__);
230            return false;
231        } else if (!isset($this->_params['subject'])) {
232            App::logMsg(sprintf('Cannot send email. SUBJECT not defined.', null), LOG_ERR, __FILE__, __LINE__);
233            return false;
234        }
235
236        // Wrap email text body, using _template_replaced if replacements have been used, or just a fresh _template if not.
237        $final_body = wordwrap(isset($this->_template_replaced) ? $this->_template_replaced : $this->_template);
238
239        // Ensure all placeholders have been replaced. Find anything with {...} characters.
240        if (preg_match('/({[^}]+})/', $final_body, $unreplaced_match)) {
241            App::logMsg(sprintf('Cannot send email. Variables left unreplaced in template: %s', (isset($unreplaced_match) ? $unreplaced_match[1] : '')), LOG_ERR, __FILE__, __LINE__);
242            return false;
243        }
244       
245        // Final "to" header can have multiple addresses if in an array.
246        $final_to = is_array($this->_params['to']) ? join(', ', $this->_params['to']) : $this->_params['to'];
247       
248        // From headers are custom headers.
249        $headers = sprintf("From: %s\r\n", $this->_params['from']);
250       
251        // This is the address where delivery problems are sent to. We must strip off everything except the local@domain part.
252        $envelope_sender_header = sprintf('-f %s', preg_replace('/^.*<?([^\s@\[\]<>()]+\@[A-Za-z0-9.-]{1,}\.[A-Za-z]{2,5})>?$/iU', '$1', $this->_params['from']));
253
254        // Ensure message was successfully accepted for delivery.
255        if (!mail($final_to, $this->_params['subject'], $final_body, $headers, $envelope_sender_header)) {
256            App::logMsg(sprintf('Email failure with parameters: %s, %s, %s, %s', $this->_params['to'], $this->_params['subject'], str_replace("\r\n", '', $headers), $envelope_sender_header), LOG_NOTICE, __FILE__, __LINE__);
257            return false;
258        }
259       
260        return true;
261    }
262   
263    /**
264     * Validates an email address based on the recommendations in RFC 3696.
265     * Is more loose than restrictive, to allow the many valid variants of
266     * email addresses while catching the most common mistakes. Checks an array too.
267     * http://www.faqs.org/rfcs/rfc822.html
268     * http://www.faqs.org/rfcs/rfc2822.html
269     * http://www.faqs.org/rfcs/rfc3696.html
270     * http://www.faqs.org/rfcs/rfc1035.html
271     *
272     * @access  public
273     * @param   mixed  $email  Address to check, string or array.
274     * @return  bool    Validity of address.
275     * @author  Quinn Comendant <quinn@strangecode.com>
276     * @since   30 Nov 2005 22:00:50
277     */
278    function validEmail($email)
279    {
280        // If an array, check values recursively.
281        if (is_array($email)) {
282            foreach ($email as $e) {
283                if (!$this->validEmail($e)) {
284                    return false;
285                }
286            }
287            return true;
288        } else {
289            // To be valid email address must match regex and fit within the lenth constraints.
290            if (preg_match($this->getParam('regex'), $email, $e_parts) && strlen($e_parts[2]) < 64 && strlen($e_parts[3]) < 255) {
291                return true;
292            } else {
293                App::logMsg(sprintf('Invalid email: %s', $email), LOG_DEBUG, __FILE__, __LINE__);
294                return false;
295            }
296        }
297    }
298}
299
300?>
Note: See TracBrowser for help on using the repository browser.