SWiFT + TWIG loop different senders - php

We have a script that monitors the database. In the database JSON data and fromName and fromEmail are stored. If the handledAt timestamp is null the email still has to be send. So we have created a cronjob that monitors the handledAt column. If it is null the email should be sent.
Our scripts works fine, however it is a bit slow. Therefore I was wondering if we are doing it in a proper manner.
Grab All emails that need to be sent
foreach($ResultSelectEmails AS $key => $value)
{
require_once '../library/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_String();
$twig = new Twig_Environment($loader, array(
'cache' => '../tmp/cache/'
));
$Contents = $twig->render(stripslashes($Templ['templateHTML']), $EData);
$Subject = $twig->render(stripslashes($Templ['templateSubject']), $EData);
$WriteFile = fopen('logs/'.$PDFLogFile.'.html','w');
fwrite($WriteFile, $Contents);
fclose($WriteFile);
// Create the Transport
$transport = Swift_SmtpTransport::newInstance('mail.example.com', 25)
->setUsername('info#example.com')
->setPassword('examplePassword')
;
// Create the Mailer using your created Transport
$mailer = Swift_Mailer::newInstance($transport);
$message = Swift_Message::newInstance($Subject)
->setFrom(array($ResultSelectEmails[$key]['fromEmail'] => $ResultSelectEmails[$key]['fromName']))
->setTo(array($ResultSelectEmails[$key]['toEmail']))
->setBody($Contents, 'text/html', 'UTF-8')
;
if(isset($EmailAttachements))
{
foreach($EmailAttachements AS $key => $value)
$message->attach(Swift_Attachment::fromPath($value));
}
$result = $mailer->send($message);
}
In the script we create a separate transport for each email. Sometimes the mail server throws an error. Mainly that it can't login.
Can we improve the script performance?
Looking forward to your thoughts....

I see some calls that you shouldn't do in a foreach loop. I don't know how many mails you want to send but this is not optimal. As you're not processing the loop in parallel you can move a few lines outside which should prevent creating some instances over and over again. I don't know the exact purpose of your code and without any details it's hard to say if this is a noticable improvement, but you could give it a try:
require_once '../library/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_String();
$twig = new Twig_Environment($loader, array(
'cache' => '../tmp/cache/'
));
// Create the Transport
$transport = Swift_SmtpTransport::newInstance('mail.example.com', 25)
->setUsername('info#example.com')
->setPassword('examplePassword')
;
// Create the Mailer using your created Transport
$mailer = Swift_Mailer::newInstance($transport);
foreach ($ResultSelectEmails AS $key => $value) {
$Contents = $twig->render(stripslashes($Templ['templateHTML']), $EData);
$Subject = $twig->render(stripslashes($Templ['templateSubject']), $EData);
$WriteFile = fopen('logs/' . $PDFLogFile . '.html', 'w');
fwrite($WriteFile, $Contents);
fclose($WriteFile);
$message = Swift_Message::newInstance($Subject)
->setFrom(array($ResultSelectEmails[$key]['fromEmail'] => $ResultSelectEmails[$key]['fromName']))
->setTo(array($ResultSelectEmails[$key]['toEmail']))
->setBody($Contents, 'text/html', 'UTF-8')
;
if (isset($EmailAttachements)) {
foreach ($EmailAttachements AS $key => $value)
$message->attach(Swift_Attachment::fromPath($value));
}
$result = $mailer->send($message);
}

Related

php-mailparse composer not loading on server

I have an Ubuntu php7.0 server and I am trying to use a php Mailparse script I found here
However I confirmed that composer is installed on the server and mailparse is also on the server. However the below script returns a 500 error and I tracked it down to the top two lines of code that is causing it and not sure how to resolve it.
So what I mean is when I comment out the two lines that say
require_once __DIR__.'/vendor/autoload.php';
$Parser = new PhpMimeMailParser\Parser();
Then the script will load but of course the mail parse wont work??
//load the php mime parse library
require_once __DIR__.'/vendor/autoload.php';
$Parser = new PhpMimeMailParser\Parser();
//Include the AWS SDK using the Composer autoloader.
require 'awssdk/aws-autoloader.php';
use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;
// AWS Info
$bucketName = 'pipedemail';
$IAM_KEY = '******';
$IAM_SECRET = '******';
// Connect to AWS
try {
// You may need to change the region. It will say in the URL when the bucket is open
// and on creation. us-east-2 is Ohio, us-east-1 is North Virgina
$s3 = S3Client::factory(
array(
'credentials' => array(
'key' => $IAM_KEY,
'secret' => $IAM_SECRET
),
'version' => 'latest',
'region' => 'us-east-1'
)
);
} catch (Exception $e) {
// We use a die, so if this fails. It stops here. Typically this is a REST call so this would
// return a json object.
die("Error: " . $e->getMessage());
}
// Use the high-level iterators (returns ALL of your objects).
$objects = $s3->getIterator('ListObjects', array('Bucket' => $bucketName));
foreach ($objects as $object)
{
$objectkey = $object['Key'];
$path = "https://s3.amazonaws.com/pipedemail/$objectkey";
//lets get the raw email file to parse it
$Parser->setText(file_get_contents($path));
// Once we've indicated where to find the mail, we can parse out the data
$to = $Parser->getHeader('to'); // "test" <test#example.com>, "test2" <test2#example.com>
$addressesTo = $Parser->getAddresses('to'); //Return an array : [[test, test#example.com, false],[test2, test2#example.com, false]]
$from = $Parser->getHeader('from'); // "test" <test#example.com>
$addressesFrom = $Parser->getAddresses('from'); //Return an array : test, test#example.com, false
$subject = $Parser->getHeader('subject');
}

Gmail API with PHP mailer can't send multiple attachments

I am using PHPmailer with Gmail API to send out mail. This process has worked very well for me for sending just standard emails, however, I want to also have the ability to send out an email with attachments using the Gmail API. When I tried using $mail->addAttachment($urlString, $name); Gmail would come back with the error Request Entity Too Large Error 413 (The size of the attachments never goes above 20MB so it should be well within the 35MB limits of Gmail API).
After some searching, I found out it was because I wasn't using "/Upload URI" for sending large Gmail Attachments (Anything above 5mb and below 35mb). Problem is, I am not very knowledgeable on how to work the Gmail API and only got what I have now from basically copying code from somewhere else and slightly modifying it for my uses and as such, I have no idea how to change the URI like that.
Here is what I have so far, that works with standard emails:
function($agent, $assistantName='', $assistantEmail='', $subject, $body, $attachments=''){
$key = realpath(dirname(__FILE__).'/Services/Google/Gmail.json');
$useremail = 'myuseremail#example.com';
$toAddress = $agent->email;
$agentFirst = $agent->first_name;
$client = new Google_Client();
putenv('GOOGLE_APPLICATION_CREDENTIALS='.$key);
$client->useApplicationDefaultCredentials();
$user_to_impersonate = $useremail;
$client->setSubject($user_to_impersonate);
$client->addScope('https://www.googleapis.com/auth/gmail.compose');
if ($client->isAccessTokenExpired()) {
$client->refreshTokenWithAssertion();
}
//prepare the mail with PHPMailer
$mail = new PHPMailer();
$mail->CharSet = "UTF-8";
$mail->Encoding = "base64";
$subject = $subject;
$msg = $body;
$from = $useremail;
$fname = "My Name";
$mail->From = $from;
$mail->FromName = $fname;
$mail->AddAddress($toAddress, $agentFirst);
$mail->AddCC($assistantEmail, $assistantName);
$mail->AddReplyTo($from,$fname);
if ($attachments != '') {
foreach ($attachments as $name => $urlString) {
$mail->addAttachment($urlString, $name);
}
}
$mail->Subject = $subject;
$mail->Body = $msg;
$mail->IsHTML(true);
$mail->preSend();
$mime = $mail->getSentMIMEMessage();
$m = new Google_Service_Gmail_Message();
$data = base64_encode($mime);
$data = str_replace(array('+','/','='),array('-','_',''),$data); // url safe
$m->setRaw($data);
$service = new Google_Service_Gmail($client);
$email = $service->users_messages->send($useremail, $m);
return json_encode($email);
}
I don't really know where to go from here, so any help would be appreciated.
use EZCMail and build the email structure yourself... it is very pickey! I can post some details after.
you may also have to send the email in chunks.
If the email is over 4MB then you are going to have to send in chunks using Google_Http_MediaFileUpload
Your code using this should be something similar to this, there is examples for using Google_Http_MediaFileUpload elsewhere on the web which might be more complete:
$client->setDefer(true);
// Call the API with the media upload, defer so it doesn't immediately return.
$arrRequestParams = $arrRequestParams+['uploadType' => 'multipart'];
$result = $this->TransportPrepSwitch($strAction, $objGMessage, $arrRequestParams); // Make draft or message $service->users_messages->send('me', $objGMessage, $arrRequestParams);
$mailMessage = base64url_decode($strRaw);
// create mediafile upload
$chunkSizeBytes = 1*1024*1024; // send to google in 1MB chunks
$media = new Google_Http_MediaFileUpload($client,$result,'message/rfc822',$mailMessage,true,$chunkSizeBytes);
$media->setFileSize(strlen($mailMessage));
// Upload chunks. Status will contain the completed $result.
$status = false;
set_time_limit(300);
while(!$status)
$status = $media->nextChunk();
// Reset to the client to execute requests immediately in the future.
$client->setDefer(false);
$objResponce = $status;
Also the structure of the email parts MUST be as follows:
multipart/mixed => [
multipart/related => [
multipart/alternative => [
plain,
html
],
inline images,
],
attachments,
]
The only way I could achieve this was using EZCMail to build the email part by part.

Error sending more than 300 emails with PHP Swift Mailer showing Internal Server Error

Error sending more than 300 emails with PHP Swift Mailer showing Internal Server Error getting only 200 mails out of 300
i am using ftp surfer
another problem is after sending 200 mails upto one hour mails are not going it showing us as send but i am not getting any mails after one hour again they are getting
mail ids are getting from mysql database
using for each loop mails are sending
<?php // Create the message
// Create the message
$message = Swift_Message::newInstance();
$sql=mysql_query("select * from test_email where status=''");
while($row=mysql_fetch_array($sql))
{
// Usually you want to replace the following static array
// with a dynamic one built retrieving users from the database
$users = array(
array(
"fullname" => "Aurelio De Rosa",
"operations" => $row['sno'],
"email" => $row['email']
)
);
// Create the replacements array
$replacements = array();
foreach ($users as $user) {
$replacements[$user["email"]] = array (
"{fullname}" => $user["fullname"],
"{transactions}" => $user["operations"]
);
}
// Create the mail transport configuration
$transport = Swift_MailTransport::newInstance();
// Create an instance of the plugin and register it
$plugin = new Swift_Plugins_DecoratorPlugin($replacements);
$mailer = Swift_Mailer::newInstance($transport);
$mailer->registerPlugin($plugin);
// Create the message
$message = Swift_Message::newInstance();
$message->setSubject("This email is sent using Swift Mailer");
// Create your file contents in the normal way, but don't write them to disk
// Create your file contents in the normal way, but don't write
//them to disk
$type = $message->getHeaders()->get('Content-Type');
$type->setValue('text/html');
$type->setParameter('charset', 'utf-8');
// Use AntiFlood to re-connect after 100 emails
$mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100));
// And specify a time in seconds to pause for (30 secs)
$mailer->registerPlugin(new Swift_Plugins_AntiFloodPlugin(100, 30));
// Create the message
$message = Swift_Message::newInstance('My subject');
// Set the body
$message->setBody(
'<html>' .
' <head></head>' .
' <body>' .
' Here is an image <img src="http://www.graymatter8.info/Developer1/swiftm/x.php?ts={transactions}&&ip='.$ip.'" alt="" width="1" height="1" border="0"/>' .
' Rest of message' .
' </body>' .
'</html>',
'text/html' // Mark the content-type as HTML
);
$message->setFrom("account#bank.com", "Testing");
$failedRecipients = array();
$numSent = 0;
// Send the email
foreach($users as $user)
{
$message->setTo($user["email"], $user["fullname"]);
$mailer->send($message);
}
?>
You should send it out in packets, like 10-20 at once. As soon as a package is done, you can go to the next 10-20, and so on...

send email with attached files in ZF2

How to send email with text/plain, text/html and attaches in zf2 ?
I use this code to send email with smtp:
$files = $this->params()->fromFiles();
$smtp = new \Zend\Mail\Transport\Smtp();
$smtp->setAutoDisconnect(true);
$optn = new \Zend\Mail\Transport\SmtpOptions(array(
'host' => 'mail.myserver.com',
'connection_class' => 'login',
'connection_config' => array(
'username' => 'user#myserver.com',
'password' => 'mypassword',
),
));
$smtp->setOptions($optn);
$htmlPart = new \Zend\Mime\Part('<p>some html</p>');
$htmlPart->type = Mime::TYPE_HTML;
$textPart = new \Zend\Mime\Part('some text');
$textPart->type = Mime::TYPE_TEXT;
$i=0;
$attaches = array();
foreach($files as $file){
if ($file['error'])
continue;
$attaches[$i] = new \Zend\Mime\Part(file_get_contents($file['tmp_name']));
$attaches[$i]->type = $file['type'].'; name="'.$file['name'].'"';
$attaches[$i]->encoding = 'base64';
$attaches[$i]->disposition = 'attachment';
$attaches[$i]->filename = $file['name'];
$i++;
}
$parts = array();
if (count($attaches)>0) {
$parts = array_merge(array($textPart,$htmlPart),$attaches);
$type = Mime::MULTIPART_MIXED;
}
else{
$parts = array($textPart, $htmlPart);
$type = Mime::MULTIPART_ALTERNATIVE ;
}
$body = new \Zend\Mime\Message();
$body->setParts($parts);
$message = new \Zend\Mail\Message();
$message->setFrom('user#myserver.com');
$message->addTo('receiver#myserver.com');
$message->setSubject('subject');
$message->setEncoding("UTF-8");
$message->setBody($body);
$message->getHeaders()->get('content-type')->setType($type);
$smtp->send($message);
If I attach files, it sends files and contents but it shows plain and html text together in receiver inbox:
<p>some html</p>
some text
When I don't attach any files, it shows html text singly:
some html
Any help?
Currently there is no easy way in ZF2 (2.2) to combine a multipart/alternative body (html with text alternative for clients that cannot/do-not-want-to use html) with attachments.
If you add the 'multipart/alternative' content-type header to the entire message, in some email clients the attachment (link) will not be displayed.
The solution is to split the message in two, the body (text and html) and the attachment:
http://jw-dev.blogspot.com.es/2013/01/zf2-zend-mail-multipartalternative-and.html
an example:
$content = new MimeMessage();
$htmlPart = new MimePart("<html><body><p>Sorry,</p><p>I'm going to be late today!</p></body></html>");
$htmlPart->type = 'text/html';
$textPart = new MimePart("Sorry, I'm going to be late today!");
$textPart->type = 'text/plain';
$content->setParts(array($textPart, $htmlPart));
$contentPart = new MimePart($content->generateMessage());
$contentPart->type = 'multipart/alternative;' . PHP_EOL . ' boundary="' . $content->getMime()->boundary() . '"';
$attachment = new MimePart(fopen('/path/to/test.pdf', 'r'));
$attachment->type = 'application/pdf';
$attachment->encoding = Mime::ENCODING_BASE64;
$attachment->disposition = Mime::DISPOSITION_ATTACHMENT;
$body = new MimeMessage();
$body->setParts(array($contentPart, $attachment));
$message = new Message();
$message->setEncoding('utf-8')
->addTo('mywife#home.com')
->addFrom('myself#office.com')
->setSubject('will be late')
->setBody($body);
$transport = new SmtpTransport();
$options = new SmtpOptions($transportConfig),
));
$transport->setOptions($options);
$transport->send($message);
For the above you would need the following use statements:
use Zend\Mail\Message;
use Zend\Mail\Transport\Smtp as SmtpTransport;
use Zend\Mail\Transport\SmtpOptions;
use Zend\Mime\Mime;
use Zend\Mime\Part as MimePart;
use Zend\Mime\Message as MimeMessage;
ZF1 had a _buildBody() method in Zend_Mail_Transport_Abstract which did this automatically.
I have found it a better solution so I am writing it.
Namespace YourNamesapace;
use Zend\Mail\Message as ZendMessage;
use Zend\Mime\Part as MimePart;
use Zend\Mime\Message as MimeMessage;
use Zend\Mail\Transport\Sendmail;
class Testmail
{
public static function sendMailWithAttachment($to, $subject, $htmlMsg, $dir, $fileName)
{
$fileFullPath = $dir . '/' . $fileName;
// Render content from template
$htmlContent = $htmlMsg;
// Create HTML part
$htmlPart = new MimePart($htmlContent);
$htmlPart->type = "text/html";
// Create plain text part
$stripTagsFilter = new \Zend\Filter\StripTags();
$textContent = str_ireplace(array("<br />", "<br>"), "\r\n", $htmlContent);
$textContent = $stripTagsFilter->filter($textContent);
$textPart = new MimePart($textContent);
$textPart->type = "text/plain";
// Create separate alternative parts object
$alternatives = new MimeMessage();
$alternatives->setParts(array($textPart, $htmlPart));
$alternativesPart = new MimePart($alternatives->generateMessage());
$alternativesPart->type = "multipart/alternative;\n boundary=\"".$alternatives->getMime()->boundary()."\"";
$body = new MimeMessage();
$body->addPart($alternativesPart);
$attachment = new MimePart( file_get_contents($fileFullPath) );
$attachment->type = \Zend\Mime\Mime::TYPE_OCTETSTREAM;
$attachment->filename = basename($fileName);
$attachment->disposition = \Zend\Mime\Mime::DISPOSITION_ATTACHMENT;
$attachment->encoding = \Zend\Mime\Mime::ENCODING_BASE64;
$body->addPart($attachment);
// Create mail message
$mailMessage = new ZendMessage();
$mailMessage->setFrom('noreply#example.com', 'from Name');
$mailMessage->setTo($to);
$mailMessage->setSubject($subject);
$mailMessage->setBody($body);
$mailMessage->setEncoding("UTF-8");
$mailMessage->getHeaders()->get('content-type')->setType('multipart/mixed');
$transport = new Sendmail();
$transport->send($mailMessage);
}
}
Set the type from :
$attaches[$i]->type = $file['type'].'; name="'.$file['name'].'"';
To:
$attaches[$i]->type = \Zend\Mime\Mime::TYPE_OCTETSTREAM;
You will also want to confirm that if you are using an SMTP service that they allow attachements through the protocol.
E-Mail Messages with Attachments
$mail = new Zend\Mail\Message();
// build message...
$mail->createAttachment($someBinaryString);
$mail->createAttachment($myImage,
'image/gif',
Zend\Mime\Mime::DISPOSITION_INLINE,
Zend\Mime\Mime::ENCODING_BASE64);
If you want more control over the MIME part generated for this attachment you can use the return value of createAttachment() to modify its attributes. The createAttachment() method returns a Zend\Mime\Part object:
$mail = new Zend\Mail\Message();
$at = $mail->createAttachment($myImage);
$at->type = 'image/gif';
$at->disposition = Zend\Mime\Mime::DISPOSITION_INLINE;
$at->encoding = Zend\Mime\Mime::ENCODING_BASE64;
$at->filename = 'test.gif';
$mail->send();
An alternative is to create an instance of Zend\Mime\Part and add it with addAttachment():
$mail = new Zend\Mail\Message();
$at = new Zend\Mime\Part($myImage);
$at->type = 'image/gif';
$at->disposition = Zend\Mime\Mime::DISPOSITION_INLINE;
$at->encoding = Zend\Mime\Mime::ENCODING_BASE64;
$at->filename = 'test.gif';
$mail->addAttachment($at);
$mail->send();
Reference1
Reference2
Reference3

Error when Sending Mail with Attachments Using PEAR

My page continues to error out (Error 324 - Chrome) when attempting to send e-mails with attachments using PHP's PEAR Mail extension. Although the page error's out - I do receive one of the approximately 800 e-mails. Here's what I'm working with:
function email_statements($type) {
switch($type) {
// Determine the type of statement to be e-mailed
case 'monthly':
// Array holding all unique AP e-mail addresses
$ap_email_addresses = array();
include('../path/to/db/connection/params.php');
// Grab the unique AP e-mail address set to receive statements
$stmt = $mysqli->prepare("SELECT email FROM statements GROUP BY email ORDER BY email ASC");
$stmt->execute();
$stmt->bind_result($ap_email_address);
// Add unique e-mail address to AP E-mail Addresses array
while($row = $stmt->fetch()) $ap_email_addresses[] = $ap_email_address;
$stmt->close();
$mysqli->close();
// Verify we grabbed the e-mail addresses
if(count($ap_email_addresses) == 0) {
// Exit and return error code if unable to grab e-mail addresses
$return_message = 1;
exit;
}
// E-mail addresses grabbed - proceed
else {
// PDF formatted date
date_default_timezone_set('America/New_York');
$formatted_date = date('m_d_Y');
// Now we have the unique e-mail addresses - associate those with the account numbers
include('../path/to/db/connection/params.php');
foreach($ap_email_addresses as $email_address) {
$file_names = array();
$stmt = $mysqli->prepare("SELECT customer_number FROM statements WHERE email = ?");
$stmt->bind_param("s", $email_address);
$stmt->execute();
$stmt->bind_result($ap_account_number);
// Constructs the name of the statement (PDF) file to be sent
while($output = $stmt->fetch()) $file_names[] = $ap_account_number . '_' . $formatted_date . '.pdf';
// Send e-mails
include('Mail.php');
include('Mail/mime.php');
// Include SMTP authentication parameters
include('../path/to/email/info.php');
// Set the e-mail recipients
$recipients = 'example#example.com';
// Set the e-mail subject
$subject = 'Monthly Account Statement';
// Create the e-mail body
$html =
'<html>
<body>
<p>Test E-mail</p>
</body>
</html>';
// Construct the message headers
$headers = array(
'From' => $from,
'Subject' => $subject,
'Content-Type' => 'text/html; charset=UTF-8',
'MIME-Version' => '1.0'
);
$mimeparams = array();
$mimeparams['text_encoding'] = '8bit';
$mimeparams['text_charset'] = 'UTF-8';
$mimeparams['html_charset'] = 'UTF-8';
$mimeparams['head_charset'] = 'UTF-8';
// Create a new instance
$mime = new Mail_mime();
// Specify the e-mail body
$mime->setHTMLBody($html);
// Attach the statements (PDF)
foreach($file_names as $attached_file) {
$file = '../path/to/the/pdf/file/' . $attached_file;
$file_name = $attached_file;
$content_type = "Application/pdf";
$mime->addAttachment ($file, $content_type, $file_name, 1);
}
// Create the body
$body = $mime->get($mimeparams);
// Add the headers
$headers = $mime->headers($headers);
// Create SMTP connect array to be passed
$smtp = Mail::factory('smtp', array ('host' => $host, 'port' => $port, 'auth' => true, 'username' => $username, 'password' => $password));
// Send the message
$mail = $smtp->send($recipients, $headers, $body);
if(PEAR::isError($mail)) echo 'Error';
else echo 'Sent';
// Close this account and cycle through the rest
$stmt->close();
}
$mysqli->close();
}
break;
}
}
Now I thought maybe I wasn't giving the script enough time to execute - so I set it ridiculously high set_time_limit(99999999) to ensure it had plenty of time, although it normally times out within 10-15 seconds. So basically it works like this, I have an internal DB that stores customer account numbers and e-mail addresses. Accounts can have the same e-mail address (It's sent to their company's AP department) - aka one e-mail address receives statements for multiple branches. From their each statement is already constructed with the format being ACCOUNTNUMBER_MM_DD_YYYY.pdf.
So I'm simply trying to have a short message in the body, and attach the monthly statements. Again, not sure why it's failing, and even though the script halts, I do receive ONE of the e-mails (I'm sending them all to myself at the moment just to test). Can anyone see where I may have an issue?
I discovered the issue. As of PHP 5.0+ you cannot have multiple instances of the same include file - aka Mail.php was included as it looped. Once it was moved outside of the loop, the issue was resolved.

Categories