Sending Email from the CakePHP Shell

Sending an email from a CakePHP shell may sound like a difficult task but, with the aid of a re-usable shell task, it's an easy feature to implement.  In this article I will walk through the setup and creation of the shell task so that you can be sending emails in minutes.

Getting Started

The first thing you will need is a shell in which to use the email sending task.  If you're not familiar with creating a shell I recommend you start by reading the section of the CakePHP manual on Creating Shells & Tasks.  I will be using, for the purpose of demonstration, a simple shell that will send out an email in both HTML and plain text format.  The task will make use of the CakePHP Email Component so if you're not familiar with how to use that I would suggest first reading that section in the manual.

Creating the Shell

The shell, as I mentioned, is quite simple and doesn't really offer a lot of functionality itself.  The shell code, below, is not meant to be "production ready" but will serve as a base to build on and demonstrate the basic usage.  Create the shell by saving the code below in the "/app/vendors/shells/" directory as "foo.php" (you can use a different name if you prefer, just remember to update the class name accordingly).

  1.  
  2. class FooShell extends Shell {
  3. /**
  4.   * Load Tasks used by the shell
  5.   *
  6.   * @access public
  7.   */
  8. public $tasks = array('Mail');
  9.  
  10. /**
  11.   * Welcome message
  12.   *
  13.   * @access public
  14.   */
  15. public function _welcome() {}
  16.  
  17. /**
  18.   * Default shell process
  19.   * This is called if no task is specified
  20.   *
  21.   * @access public
  22.   */
  23. public function main() {
  24. $this->out('Use the command: cake foo sendTestMail');
  25. }
  26.  
  27. /**
  28.   * Send an email message using the Mail task
  29.   *
  30.   * @access public
  31.   */
  32. public function sendTestMail() {
  33. $recipient = '';
  34. $subject = '';
  35. $from = '';
  36. $template = '';
  37.  
  38. /* Get the address to send to */
  39. while($recipient == '') {
  40. $recipient = $this->in('Please enter the recipient address: ');
  41. }
  42.  
  43. /* Get the subject */
  44. while($subject == '') {
  45. $subject = $this->in('Please enter a subject line for the message: ');
  46. }
  47.  
  48. /* Get the sender address */
  49. while($from == '') {
  50. $from = $this->in('Please enter your (sender) email address: ');
  51. }
  52.  
  53. /* Get the template to use */
  54. $this->out('The email template you specify below must already exist in the /views/elements/email/html and /views/elements/email/text directories.');
  55. $this->out('Enter the template name without the .ctp extension.');
  56. while($template == '') {
  57. $template = $this->in('Please enter the name of the template to use: ', null, 'default');
  58. }
  59.  
  60. /**
  61.   * If you want to use SMTP; uncomment the lines below and provide your SMTP server information.
  62.   * Leave commented to use the PHP mail feature to send the email.
  63.   */
  64. /*
  65.   $smtpOptions = array(
  66.   'host' => 'localhost', // your SMTP server
  67.   'username'=>'smtpUsername', // your SMTP account username
  68.   'password'=>'smtpPassword', // your SMTP account password
  69.   'port'=>'25', // SMTP server port to connect to. This key is optional. Default is 25.
  70.   'timeout'=>'15', // SMTP server timeout in seconds. This key is optional. Default is 15.
  71.   );
  72.  
  73.   $this->Mail->setSmtpOptions($smtpOptions);
  74.   */
  75.  
  76. /**
  77.   * Send the email
  78.   */
  79. $sent = $this->Mail->sendMail($recipient, $subject, $from, $template);
  80. }
  81. }
  82.  

Creating the Shell Task

Once you have the shell setup, the next step is to create the shell task that will actually handle the sending of the email.  This task isn't much more complicated than the sample shell presented above and makes use of the native CakePHP Email component to do most of the work of sending the email.  The code presented here does not include any handling of attachments, to keep it simple, but that shouldn't be hard to work in.

The code below will give us our shell task for sending the email.  Save this as "mail.php" in the "/app/vendors/shells/tasks/" directory.  Here, again, you can use a different name but make sure to update the class name to match.  Do not call it "Email" since this task uses the actual CakePHP Email Component and this leads to trouble (at least it did for me).

  1.  
  2. /* load a Controller for use by the Email component */
  3. App::import('Core', 'Controller');
  4. /* load the Email component */
  5. App::import('Component', 'Email');
  6.  
  7. class MailTask extends Shell {
  8. /**
  9.   * Controller used to access the view for sending mail
  10.   *
  11.   * @access private
  12.   */
  13. private $Controller = null;
  14.  
  15. /**
  16.   * CakePHP Email component
  17.   *
  18.   * @access private
  19.   */
  20. private $Email = null;
  21.  
  22. /**
  23.   * Default SMTP server options
  24.   *
  25.   * @access private
  26.   */
  27. private $smtpDefaults = array(
  28. 'port'=>'25',
  29. 'timeout'=>'15',
  30. 'host' => 'localhost',
  31. 'username' => 'smtpUsername',
  32. 'password' => 'smtpPassword',
  33. 'messageId' => true
  34. );
  35.  
  36. /**
  37.   * User defined SMTP options
  38.   * Associative array of options for smtp mailer (port, host, timeout, username, password)
  39.   *
  40.   * @access public
  41.   */
  42. public $smtpOptions = array();
  43.  
  44. /**
  45.   * Mail sending errors
  46.   *
  47.   * @access public
  48.   */
  49. public $errors = null;
  50.  
  51. /**
  52.   * Constructor
  53.   * Setup an empty Controller and the Email component
  54.   *
  55.   * @access public
  56.   * @param object $dispatch Instance of the ShellDispatcher object that loaded this script. This is passed automatically by CakePHP.
  57.   * @return void
  58.   */
  59. public function __construct(&$dispatch) {
  60. $this->Controller =& new Controller();
  61. $this->Email =& new EmailComponent(null);
  62. $this->Email->Controller = $this->Controller;
  63. parent::__construct($dispatch);
  64. }
  65.  
  66. /**
  67.   * Main task function. Shells will call this method to start the task logic
  68.   *
  69.   * @access public
  70.   */
  71. public function execute() {}
  72.  
  73. /**
  74.   * Set the user defined SMTP server options
  75.   *
  76.   * @param array $options Array of SMTP server information
  77.   * @return void
  78.   */
  79. public function setSmtpOptions($options = array()) {
  80. $this->smtpOptions = array_merge($this->smtpDefaults, $options);
  81. }
  82.  
  83. /**
  84.   * Send an email message
  85.   *
  86.   * @param string $to Email address(es) to send the message to. Use comma separated list to send to multiple addresses.
  87.   * @param string $subject Subject of the email message.
  88.   * @param string $from Email address of the message sender.
  89.   * @param string $template CakePHP email template to use for the message.
  90.   * @param mixed $msgData Optional data to be passed to the email message template. Available in the template as $msgData;
  91.   * @param string $sendAs Format to send the message as (text, html, both). Default is "both".
  92.   * @param array $cc Optional array of email addresses to CC the message to.
  93.   * @param array $bcc Optional array of email addresses to BCC the message to.
  94.   * @param string $replyTo Email address to use as the "reply to" address. Default is the $from address.
  95.   * @param string $return Optional email address to send bounce messages/errors to. Default is the $from address.
  96.   * @param string $charset Character set to use when sending the email. Default is 'UTF8'.
  97.   * @return bool True if the message is successfully sent, False if not
  98.   */
  99. public function sendmail($to, $subject, $from, $template, $msgData = null, $sendAs = 'both', $cc = null, $bcc = null, $replyTo = null, $return = null, $charset = 'UTF8') {
  100. /* reset the Email component */
  101. $this->Email->reset();
  102.  
  103. $this->Email->to = $to;
  104. $this->Email->subject = $subject;
  105. $this->Email->from = $from;
  106. $this->Email->template = $template;
  107. $this->Email->sendAs = $sendAs;
  108. if(!empty($bcc)) {
  109. $this->Email->bcc = (is_array($bcc)) ? $bcc:array($bcc);
  110. }
  111. if(!empty($cc)) {
  112. $this->Email->cc = (is_array($cc)) ? $cc:array($cc);
  113. }
  114. $this->Email->replyTo = (empty($replyTo)) ? $from:$replyTo;
  115. $this->Email->return = (empty($return)) ? $from:$return;
  116. $this->Email->charset = $charset;
  117.  
  118. /* If there is a host/username/password set use SMTP else use Mail */
  119. if(count($this->smtpOptions) > 0 && !empty($this->smtpOptions['host']) && !empty($this->smtpOptions['username']) && !empty($this->smtpOptions['password'])) {
  120. $this->Email->smtpOptions = $this->smtpOptions;
  121. $this->Email->delivery = 'smtp';
  122. } else {
  123. $this->Email->delivery = 'mail';
  124. }
  125.  
  126. /* Set the message data in the controller to make it available to the template */
  127. if(isset($msgData) && !empty($msgData)) { $this->Controller->set(compact('msgData')); }
  128.  
  129. /* send the email */
  130. $sent = $this->Email->send();
  131.  
  132. /* Store sending errors if using SMTP */
  133. if($this->Email->delivery == 'smtp' && !empty($this->Email->smtpError)) {
  134. $this->errors = $this->Email->smtpError;
  135. }
  136.  
  137. return $sent;
  138. }
  139. }
  140.  

Putting It All Together

Now that we have the shell and the task ready to go we can put them to use sending our emails.  This example will be executed by using the shell from the console but it can easily be setup to run as a cron job if you want to automate the process.

The console command to send out an email would be similar to:

  1.  
  2. cake foo sendTestMail
  3.  

If everything worked you should have an email going out to the address you specified as the recipient.  This example is simple but provides a functional, re-usable, shell task for sending out emails.  This example can easily be expanded to send out newsletters or reminders to addresses in a database or incorporated into another shell.

Something that I didn't cover here is the setup of the email templates for use by the Email component.  That is well covered in the CakePHP manual so I didn't repeat it here.  Just setup the templates as you would for using the Email component is a standard controller.