1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * Redistributions of files must retain the above copyright notice.
8: *
9: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
10: * @since 3.1.0
11: * @license https://opensource.org/licenses/mit-license.php MIT License
12: */
13: namespace Cake\Mailer;
14:
15: use Cake\Datasource\ModelAwareTrait;
16: use Cake\Event\EventListenerInterface;
17: use Cake\Mailer\Exception\MissingActionException;
18:
19: /**
20: * Mailer base class.
21: *
22: * Mailer classes let you encapsulate related Email logic into a reusable
23: * and testable class.
24: *
25: * ## Defining Messages
26: *
27: * Mailers make it easy for you to define methods that handle email formatting
28: * logic. For example:
29: *
30: * ```
31: * class UserMailer extends Mailer
32: * {
33: * public function resetPassword($user)
34: * {
35: * $this
36: * ->setSubject('Reset Password')
37: * ->setTo($user->email)
38: * ->set(['token' => $user->token]);
39: * }
40: * }
41: * ```
42: *
43: * Is a trivial example but shows how a mailer could be declared.
44: *
45: * ## Sending Messages
46: *
47: * After you have defined some messages you will want to send them:
48: *
49: * ```
50: * $mailer = new UserMailer();
51: * $mailer->send('resetPassword', $user);
52: * ```
53: *
54: * ## Event Listener
55: *
56: * Mailers can also subscribe to application event allowing you to
57: * decouple email delivery from your application code. By re-declaring the
58: * `implementedEvents()` method you can define event handlers that can
59: * convert events into email. For example, if your application had a user
60: * registration event:
61: *
62: * ```
63: * public function implementedEvents()
64: * {
65: * return [
66: * 'Model.afterSave' => 'onRegistration',
67: * ];
68: * }
69: *
70: * public function onRegistration(Event $event, Entity $entity, ArrayObject $options)
71: * {
72: * if ($entity->isNew()) {
73: * $this->send('welcome', [$entity]);
74: * }
75: * }
76: * ```
77: *
78: * The onRegistration method converts the application event into a mailer method.
79: * Our mailer could either be registered in the application bootstrap, or
80: * in the Table class' initialize() hook.
81: *
82: * @method \Cake\Mailer\Mailer setTo($email, $name = null)
83: * @method array getTo()
84: * @method \Cake\Mailer\Mailer to($email = null, $name = null)
85: * @method \Cake\Mailer\Mailer setFrom($email, $name = null)
86: * @method array getFrom()
87: * @method \Cake\Mailer\Mailer from($email = null, $name = null)
88: * @method \Cake\Mailer\Mailer setSender($email, $name = null)
89: * @method array getSender()
90: * @method \Cake\Mailer\Mailer sender($email = null, $name = null)
91: * @method \Cake\Mailer\Mailer setReplyTo($email, $name = null)
92: * @method array getReplyTo()
93: * @method \Cake\Mailer\Mailer replyTo($email = null, $name = null)
94: * @method \Cake\Mailer\Mailer setReadReceipt($email, $name = null)
95: * @method array getReadReceipt()
96: * @method \Cake\Mailer\Mailer readReceipt($email = null, $name = null)
97: * @method \Cake\Mailer\Mailer setReturnPath($email, $name = null)
98: * @method array getReturnPath()
99: * @method \Cake\Mailer\Mailer returnPath($email = null, $name = null)
100: * @method \Cake\Mailer\Mailer addTo($email, $name = null)
101: * @method \Cake\Mailer\Mailer setCc($email, $name = null)
102: * @method array getCc()
103: * @method \Cake\Mailer\Mailer cc($email = null, $name = null)
104: * @method \Cake\Mailer\Mailer addCc($email, $name = null)
105: * @method \Cake\Mailer\Mailer setBcc($email, $name = null)
106: * @method array getBcc()
107: * @method \Cake\Mailer\Mailer bcc($email = null, $name = null)
108: * @method \Cake\Mailer\Mailer addBcc($email, $name = null)
109: * @method \Cake\Mailer\Mailer setCharset($charset)
110: * @method string getCharset()
111: * @method \Cake\Mailer\Mailer charset($charset = null)
112: * @method \Cake\Mailer\Mailer setHeaderCharset($charset)
113: * @method string getHeaderCharset()
114: * @method \Cake\Mailer\Mailer headerCharset($charset = null)
115: * @method \Cake\Mailer\Mailer setSubject($subject)
116: * @method string getSubject()
117: * @method \Cake\Mailer\Mailer subject($subject = null)
118: * @method \Cake\Mailer\Mailer setHeaders(array $headers)
119: * @method \Cake\Mailer\Mailer addHeaders(array $headers)
120: * @method \Cake\Mailer\Mailer getHeaders(array $include = [])
121: * @method \Cake\Mailer\Mailer setTemplate($template)
122: * @method string getTemplate()
123: * @method \Cake\Mailer\Mailer setLayout($layout)
124: * @method string getLayout()
125: * @method \Cake\Mailer\Mailer template($template = false, $layout = false)
126: * @method \Cake\Mailer\Mailer setViewRenderer($viewClass)
127: * @method string getViewRenderer()
128: * @method \Cake\Mailer\Mailer viewRender($viewClass = null)
129: * @method \Cake\Mailer\Mailer setViewVars($viewVars)
130: * @method array getViewVars()
131: * @method \Cake\Mailer\Mailer viewVars($viewVars = null)
132: * @method \Cake\Mailer\Mailer setTheme($theme)
133: * @method string getTheme()
134: * @method \Cake\Mailer\Mailer theme($theme = null)
135: * @method \Cake\Mailer\Mailer setHelpers(array $helpers)
136: * @method array getHelpers()
137: * @method \Cake\Mailer\Mailer helpers($helpers = null)
138: * @method \Cake\Mailer\Mailer setEmailFormat($format)
139: * @method string getEmailFormat()
140: * @method \Cake\Mailer\Mailer emailFormat($format = null)
141: * @method \Cake\Mailer\Mailer setTransport($name)
142: * @method \Cake\Mailer\AbstractTransport getTransport()
143: * @method \Cake\Mailer\Mailer transport($name = null)
144: * @method \Cake\Mailer\Mailer setMessageId($message)
145: * @method bool|string getMessageId()
146: * @method \Cake\Mailer\Mailer messageId($message = null)
147: * @method \Cake\Mailer\Mailer setDomain($domain)
148: * @method string getDomain()
149: * @method \Cake\Mailer\Mailer domain($domain = null)
150: * @method \Cake\Mailer\Mailer setAttachments($attachments)
151: * @method array getAttachments()
152: * @method \Cake\Mailer\Mailer attachments($attachments = null)
153: * @method \Cake\Mailer\Mailer addAttachments($attachments)
154: * @method \Cake\Mailer\Mailer message($type = null)
155: * @method \Cake\Mailer\Mailer setProfile($config)
156: * @method string|array getProfile()
157: * @method \Cake\Mailer\Mailer profile($config = null)
158: * @method \Cake\Mailer\Mailer setEmailPattern($regex)
159: * @method string getEmailPattern()
160: * @method \Cake\Mailer\Mailer emailPattern($regex = null)
161: */
162: abstract class Mailer implements EventListenerInterface
163: {
164:
165: use ModelAwareTrait;
166:
167: /**
168: * Mailer's name.
169: *
170: * @var string
171: */
172: public static $name;
173:
174: /**
175: * Email instance.
176: *
177: * @var \Cake\Mailer\Email
178: */
179: protected $_email;
180:
181: /**
182: * Cloned Email instance for restoring instance after email is sent by
183: * mailer action.
184: *
185: * @var \Cake\Mailer\Email
186: */
187: protected $_clonedEmail;
188:
189: /**
190: * Constructor.
191: *
192: * @param \Cake\Mailer\Email|null $email Email instance.
193: */
194: public function __construct(Email $email = null)
195: {
196: if ($email === null) {
197: $email = new Email();
198: }
199:
200: $this->_email = $email;
201: $this->_clonedEmail = clone $email;
202: }
203:
204: /**
205: * Returns the mailer's name.
206: *
207: * @return string
208: */
209: public function getName()
210: {
211: if (!static::$name) {
212: static::$name = str_replace(
213: 'Mailer',
214: '',
215: implode('', array_slice(explode('\\', get_class($this)), -1))
216: );
217: }
218:
219: return static::$name;
220: }
221:
222: /**
223: * Sets layout to use.
224: *
225: * @deprecated 3.4.0 Use setLayout() which sets the layout on the email class instead.
226: * @param string $layout Name of the layout to use.
227: * @return $this
228: */
229: public function layout($layout)
230: {
231: deprecationWarning(
232: 'Mailer::layout() is deprecated. Use $mailer->viewBuilder()->setLayout() instead.'
233: );
234:
235: $this->_email->viewBuilder()->setLayout($layout);
236:
237: return $this;
238: }
239:
240: /**
241: * Get Email instance's view builder.
242: *
243: * @return \Cake\View\ViewBuilder
244: */
245: public function viewBuilder()
246: {
247: return $this->_email->viewBuilder();
248: }
249:
250: /**
251: * Magic method to forward method class to Email instance.
252: *
253: * @param string $method Method name.
254: * @param array $args Method arguments
255: * @return $this|mixed
256: */
257: public function __call($method, $args)
258: {
259: $result = $this->_email->$method(...$args);
260: if (strpos($method, 'get') === 0) {
261: return $result;
262: }
263:
264: return $this;
265: }
266:
267: /**
268: * Sets email view vars.
269: *
270: * @param string|array $key Variable name or hash of view variables.
271: * @param mixed $value View variable value.
272: * @return $this
273: */
274: public function set($key, $value = null)
275: {
276: $this->_email->setViewVars(is_string($key) ? [$key => $value] : $key);
277:
278: return $this;
279: }
280:
281: /**
282: * Sends email.
283: *
284: * @param string $action The name of the mailer action to trigger.
285: * @param array $args Arguments to pass to the triggered mailer action.
286: * @param array $headers Headers to set.
287: * @return array
288: * @throws \Cake\Mailer\Exception\MissingActionException
289: * @throws \BadMethodCallException
290: */
291: public function send($action, $args = [], $headers = [])
292: {
293: try {
294: if (!method_exists($this, $action)) {
295: throw new MissingActionException([
296: 'mailer' => $this->getName() . 'Mailer',
297: 'action' => $action,
298: ]);
299: }
300:
301: $this->_email->setHeaders($headers);
302: if (!$this->_email->viewBuilder()->getTemplate()) {
303: $this->_email->viewBuilder()->setTemplate($action);
304: }
305:
306: $this->$action(...$args);
307:
308: $result = $this->_email->send();
309: } finally {
310: $this->reset();
311: }
312:
313: return $result;
314: }
315:
316: /**
317: * Reset email instance.
318: *
319: * @return $this
320: */
321: protected function reset()
322: {
323: $this->_email = clone $this->_clonedEmail;
324:
325: return $this;
326: }
327:
328: /**
329: * Implemented events.
330: *
331: * @return array
332: */
333: public function implementedEvents()
334: {
335: return [];
336: }
337: }
338: