Powerful emailing library

I noticed that Yii lacks support for such an extension, component or whatever you may call it so I created one that uses PHPMailer to serve as a main library for emailing. I keep it in components and phpmailer folder inside components/libraries/phpmailer. I can send to huge lists as it’s designed to keep the connection open and stop only when all recipients are done. Let me know what you think.


<?php


/**

 * Generic class for sending emails

 * @author manilodisan

 * 

 */


class WB_Email {


	public $phpMailerPath;


	//	TRUE/FALSE to use SMTP authentication or not

	private $_bUseSMTP = FALSE;

	//	Keeping it to TRUE, will throw exceptions if something bad

	//	happens. On FALSE, it will try to ignore errors and continue

	//	it's task

	public $bThrowExceptions = TRUE;

	//	Alternative body message for text only recipients

	public $sAltMessage = "To view the message, please use an HTML compatible email viewer!";

	

	//	Holds the recipients of the email

	private $_aRecipients = array ();

	//	Holds the attachments of the email, if any

	private $_aAttachments = array ();

	//	Sender's name

	private $_sFromName;

	//	Sender's email address

	private $_sFromEmail;

	//	Reply-to name

	private $_sReplyToName;

	//	Reply-to email address

	private $_sReplyToEmail;

	//	Default Smtp host

	private $_sSmtpHost;

	//	Default Smtp username

	private $_sSmtpUser;

	//	Default Smtp password

	private $_sSmtpPassword;

	//	Default Smtp port

	private $_sSmtpPort;

	//	Smtp authentication type: ssl, tls

	private $_sSmtpSecure;

	

	//	Sent messages counter

	private $_iSent = 0;

	//	Failed messages counter

	private $_iFailed = 0;

	//	Holds the errors

	private $_aErrors = array ();


	public function __construct () {

		//	set a default path

		$this->phpMailerPath = realpath ( dirname ( __FILE__ ) ) . '/libraries/phpmailer/';

	}


	/**

	 * Used to send the email. Should be called last

	 * @param string $subject The email subject

	 * @param string $message The email body

	 * @return boolean TRUE/FALSE

	 */

	public function send ( $subject, $message ) {

		if ( ! count ( $this->_aRecipients ) ) {

			if ( $this->bThrowExceptions ) {

				throw new CException ( "No recipients found. Email failed" );

			}

			return FALSE;

		}

		

		if ( ! file_exists ( $this->phpMailerPath . 'class.phpmailer.php' ) ) {

			throw new CException ( "PHPMailer library file not found" );

		}

		

		require_once ( $this->phpMailerPath . 'class.phpmailer.php' );


		$mail = new PHPMailer ( );

		

		if ( $this->_bUseSMTP ) {

			$mail->IsSMTP ();

			if ( $this->_sSmtpSecure != '' ) {

				$mail->SMTPSecure = $this->_sSmtpSecure;

			}

			$mail->SMTPAuth = TRUE;

			$mail->SMTPKeepAlive = TRUE;

			$mail->Host = $this->_sSmtpHost;

			$mail->Port = $this->_sSmtpPort;

			$mail->Username = $this->_sSmtpUser;

			$mail->Password = $this->_sSmtpPassword;

		}

		

		$mail->SetFrom ( $this->_sFromEmail, $this->_sFromName );

		

		if ( $this->_sReplyToEmail != '' && $this->isValidEmail ( $this->_sReplyToEmail ) ) {

			$mail->AddReplyTo ( $this->_sReplyToEmail, $this->_sReplyToName );

		}

		

		$mail->Subject = $subject;

		

		foreach ( $this->_aRecipients as $aRecipient ) {

			//	use generic or a custom method wich extracts the text from html. Default: generic message

			$mail->AltBody = $this->sAltMessage;

			//	sets the body, also replace tokens and other recipient defined search/replace operations

			$mail->MsgHTML ( $this->processTokens ( $message, $aRecipient [ 'search' ], $aRecipient [ 'replace' ] ) );

			$mail->AddAddress ( $aRecipient [ "email" ], $aRecipient [ "name" ] );

			

			foreach ( $this->_aAttachments as $aAttachment ) {

				switch ( $aAttachment [ 'type' ] ) {

					case 'inline' :

						$mail->AddStringAttachment ( $aAttachment [ "path" ] );

						break;

					default :

					case 'attachment' :

						$mail->AddAttachment ( $aAttachment [ "path" ] );

						break;

				}

			

			}

			

			if ( $mail->Send () ) {

				$this->_iSent ++;

			}

			else {

				$this->_iFailed ++;

				//	log the failed email and error. We may use this list to clean our mailing list

				$this->logError ( $aRecipient [ "email" ], $mail->ErrorInfo );

			}

			

			// Clear all addresses and attachments for next loop

			$mail->ClearAddresses ();

			$mail->ClearAttachments ();

		}

	}


	/**

	 * Adds a recipient/receiver

	 * @param string $name The name of our recipient: e.g John Doe

	 * If the recipient name is not provided, the email is used instead

	 * @param string $email The email recipient

	 * @param array $search Should we look for things to replace? Maybe some tokens, e.g. {name}, {username}

	 * @param array $replace Replace values for the above searches

	 * @return void

	 */

	public function addRecipient ( $email, $name = FALSE, $search = array (), $replace = array () ) {

		if ( $this->isValidEmail ( $email ) ) {

			$this->_aRecipients [ ] = array ( 

				

				'email' => $email, 

				'name' => ( $name ) ? $name : $email, 

				'search' => $search, 

				'replace' => $replace 

			);

		}

		else {

			if ( $this->bThrowExceptions ) {

				throw new CException ( "Email $email is not a valid recipient" );

			}

		}

	}


	/**

	 * Adds an attachment to our email

	 * @param string $path Full path to our attachment file

	 * @param string $type Type of attachment (attachment/inline)

	 * @return void

	 */

	public function addAttachment ( $path, $type = 'attachment' ) {

		if ( @file_exists ( $path ) ) {

			$this->_aAttachments [ ] = array ( 

				

				'path' => $path, 

				'type' => $type 

			);

		}

		else {

			if ( $this->bThrowExceptions ) {

				throw new CException ( "Attachment at $path does not exist. Check your path" );

			}

		}

	}


	/**

	 * Sets the 'reply to' headers. The receiver of replies should be defined here

	 * @param string $email The reply-to email address

	 * @param string $name The reply-to name: e.g John Doe

	 * If the reply-to name is not provided, the reply-to email is used instead

	 * @return void

	 */

	public function setReplyTo ( $email, $name = '' ) {

		if ( $this->isValidEmail ( $email ) ) {

			$this->_sReplyToEmail = $email;

			$this->_sReplyToName = ( $name != '' ) ? $name : $email;

		}

		else {

			if ( $this->bThrowExceptions ) {

				throw new CException ( "Email $email is not a valid reply-to email" );

			}

		}

	}


	/**

	 * Sets the 'from' headers. The sender should be set here

	 * @param string $email The sender's email address

	 * @param string $name The sender's name: e.g John Doe

	 * @return void

	 */

	public function setFrom ( $email, $name = '' ) {

		if ( $this->isValidEmail ( $email ) ) {

			$this->_sFromEmail = $email;

			$this->_sFromName = ( $name != '' ) ? $name : $email;

		}

		else {

			if ( $this->bThrowExceptions ) {

				throw new CException ( "Email $email is not a valid From email" );

			}

		}

	}


	/**

	 * Returns the number of successfully sent messages

	 * @return int

	 */

	public function getSentCount () {

		return ( int ) $this->_iSent;

	}


	/**

	 * Returns the number of failed messages

	 * @return int

	 */

	public function getFailedCount () {

		return ( int ) $this->_iFailed;

	}


	/**

	 * Sets the SMTP settings. If this is not set, the mailer will use sendmail or @mail

	 * @param string $host SMTP host

	 * @param string $user SMTP username

	 * @param string $pass SMTP password

	 * @param integer $port SMTP port, default 25

	 * @return void

	 */

	public function setSmtp ( $host, $user, $pass, $port = 25, $authType = '' ) {

		if ( $authType != '' ) {

			$this->_sSmtpSecure = $authType;

		}

		$this->_bUseSMTP = TRUE;

		$this->_sSmtpHost = $host;

		$this->_sSmtpPort = $port;

		$this->_sSmtpUser = $user;

		$this->_sSmtpPassword = $pass;

	}


	/**

	 * Search replace for tokens. Either way, should output the message body

	 * @param string $message Message body

	 * @param mixed $search Array of tokens. Could be added as string as well

	 * @param mixed $replace Array of replacements. Should be string if search is string

	 * @return void

	 */

	private function processTokens ( $message, $search, $replace ) {

		if ( ( is_array ( $search ) && ! is_array ( $replace ) ) || ( is_array ( $replace ) && ! is_array ( $search ) ) || count ( $replace ) != count ( $search ) ) {

			if ( $this->bThrowExceptions ) {

				throw new CException ( "Tokens are not well formatted" );

			}

			return $message;

		}

		

		if ( is_array ( $search ) ) {

			ksort ( $search );

			ksort ( $replace );

			

			foreach ( $search as $k => $v ) {

				$search [ $k ] = '/' . preg_quote ( $v ) . '/';

			}

			return preg_replace ( $search, $replace, $message );

		}

		return preg_replace ( '/' . preg_quote ( $search ) . '/', $replace, $message );

	}


	/**

	 * Private method for validating email addresses

	 * @param string $email The email address to be validated

	 * @return boolean TRUE/FALSE based on the result

	 */

	private function isValidEmail ( $email ) {

		return ( ! preg_match ( "/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/ix", $email ) ) ? FALSE : TRUE;

	}


	/**

	 * If an email fails to one of the recipients, call this function to log the error

	 * We might use this to clean our mailing lists

	 * @param string $email The email address associated with this error

	 * @param string $error The error message

	 * @return void

	 */

	private function logError ( $email, $error ) {

		$this->_aErrors [ $email ] = $error;

	}


}

Usage example:


$subject = "I'm testing my new email";

$message = "Hello {name}, This is my test email. Your email is {email}";


try {

	$email = new WB_Email ();

	$email->setFrom ( 'my@email.com', 'John Doe' );

	//	optional

	$email->setReplyTo ( 'my@email.com', 'John Doe' );


	//	Adding this line will force the emailer to use smtp for authentication, 

	//	otherwise it will use sendmail or the mail function

	$email->setSmtp ( 'smtp.gmail.com', 'username@gmail.com', 'password', 587, 'tls' );


	//	simple example

	$search = array ( '{name}', '{email}' );

	$replace = array ( 'My name', 'myemail@mydomain.com' );

	$email->addRecipient ('test@yahoo.com', 'Jimmy Whoo', $search, $replace );

/*

	//	advanced example, recipients are pulled from fictive database

	$query = mysql_query ( 'SELECT email, name FROM users' );




	while ( $row = mysql_fetch_array ( $result ) ) {

		$search = array ( '{name}', '{email}' );

		$replace = array ( $row [ 'name' ], $row [ 'email' ] );


		$email->addAttachment ( realpath ( dirname ( __FILE__ ) ) . '/picture.jpg', 'inline' );


		$email->addRecipient ( $row [ 'email' ], $row [ 'name' ], $search, $replace );


	}

*/

	//	Should be called last

	$email->send ( $subject, $message );


	echo "Sent messages: " . $email->getSentCount () . "\n";

	echo "Failed messages: " . $email->getFailedCount () . "\n";

//	catch exceptions from PHPMailer

} catch ( phpmailerException $e ) {

	echo '<b>PHPMailer Error: ' . $e->errorMessage () . "</b>\n";

//	catch exceptions from our Email class

} catch ( CException $e ) {

	echo '<b>Emailer Error: ' . $e->getMessage () . "</b>\n";

}

Thanks for the beautiful component.

I am new to yii and php.

Can anyone explain me how to use this component as a console application.

The sample is actually enough - put it in your console command class and you should be sending emails.

You need to ask specific questions, if you run into specific problems…

I am using YiiMail myself - the extension repository has quite a few email extensions:

http://www.yiiframework.com/search/?q=mail&type=extension〈=

Thank you very much for your suggeston sir.

After writing console command "EmailCommand" in Command directory and tried to run using

yiic Email on the command prompt

I got the response

[color="#8B0000"]Yii command runner (based on Yii v1.1.6)

Usage: C:\Program Files\Zend\Apache2\htdocs\yii\framework\yiic <command-name> [parameters…]

The following commands are available:

  • message

  • migrate

  • shell

  • webapp[/color]

But when I created an entry script console.php and tried to run using

php console.php Email

It worked perfectly

Also when I copied "EmailCommand" to Command/shell directory and tried executing using the yiic shell it worked perfectly.

Can you let me know why it does not work when I tried to execute using

yiic Email on the command prompt.

Please pardon me for my English. Also I am a beginner in php and yii

You’re doing it the right way. :)

It’s what I do.

You need to run a ConsoleApplication instance, which is what you do through ‘console.php’.

Thank you Sir.