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: use ModelAwareTrait;
165:
166: /**
167: * Mailer's name.
168: *
169: * @var string
170: */
171: public static $name;
172:
173: /**
174: * Email instance.
175: *
176: * @var \Cake\Mailer\Email
177: */
178: protected $_email;
179:
180: /**
181: * Cloned Email instance for restoring instance after email is sent by
182: * mailer action.
183: *
184: * @var \Cake\Mailer\Email
185: */
186: protected $_clonedEmail;
187:
188: /**
189: * Constructor.
190: *
191: * @param \Cake\Mailer\Email|null $email Email instance.
192: */
193: public function __construct(Email $email = null)
194: {
195: if ($email === null) {
196: $email = new Email();
197: }
198:
199: $this->_email = $email;
200: $this->_clonedEmail = clone $email;
201: }
202:
203: /**
204: * Returns the mailer's name.
205: *
206: * @return string
207: */
208: public function getName()
209: {
210: if (!static::$name) {
211: static::$name = str_replace(
212: 'Mailer',
213: '',
214: implode('', array_slice(explode('\\', get_class($this)), -1))
215: );
216: }
217:
218: return static::$name;
219: }
220:
221: /**
222: * Sets layout to use.
223: *
224: * @deprecated 3.4.0 Use setLayout() which sets the layout on the email class instead.
225: * @param string $layout Name of the layout to use.
226: * @return $this
227: */
228: public function layout($layout)
229: {
230: deprecationWarning(
231: 'Mailer::layout() is deprecated. Use $mailer->viewBuilder()->setLayout() instead.'
232: );
233:
234: $this->_email->viewBuilder()->setLayout($layout);
235:
236: return $this;
237: }
238:
239: /**
240: * Get Email instance's view builder.
241: *
242: * @return \Cake\View\ViewBuilder
243: */
244: public function viewBuilder()
245: {
246: return $this->_email->viewBuilder();
247: }
248:
249: /**
250: * Magic method to forward method class to Email instance.
251: *
252: * @param string $method Method name.
253: * @param array $args Method arguments
254: * @return $this|mixed
255: */
256: public function __call($method, $args)
257: {
258: $result = $this->_email->$method(...$args);
259: if (strpos($method, 'get') === 0) {
260: return $result;
261: }
262:
263: return $this;
264: }
265:
266: /**
267: * Sets email view vars.
268: *
269: * @param string|array $key Variable name or hash of view variables.
270: * @param mixed $value View variable value.
271: * @return $this
272: */
273: public function set($key, $value = null)
274: {
275: $this->_email->setViewVars(is_string($key) ? [$key => $value] : $key);
276:
277: return $this;
278: }
279:
280: /**
281: * Sends email.
282: *
283: * @param string $action The name of the mailer action to trigger.
284: * @param array $args Arguments to pass to the triggered mailer action.
285: * @param array $headers Headers to set.
286: * @return array
287: * @throws \Cake\Mailer\Exception\MissingActionException
288: * @throws \BadMethodCallException
289: */
290: public function send($action, $args = [], $headers = [])
291: {
292: try {
293: if (!method_exists($this, $action)) {
294: throw new MissingActionException([
295: 'mailer' => $this->getName() . 'Mailer',
296: 'action' => $action,
297: ]);
298: }
299:
300: $this->_email->setHeaders($headers);
301: if (!$this->_email->viewBuilder()->getTemplate()) {
302: $this->_email->viewBuilder()->setTemplate($action);
303: }
304:
305: $this->$action(...$args);
306:
307: $result = $this->_email->send();
308: } finally {
309: $this->reset();
310: }
311:
312: return $result;
313: }
314:
315: /**
316: * Reset email instance.
317: *
318: * @return $this
319: */
320: protected function reset()
321: {
322: $this->_email = clone $this->_clonedEmail;
323:
324: return $this;
325: }
326:
327: /**
328: * Implemented events.
329: *
330: * @return array
331: */
332: public function implementedEvents()
333: {
334: return [];
335: }
336: }
337: