Link to home
Start Free TrialLog in
Avatar of Crazy Horse
Crazy HorseFlag for South Africa

asked on

Sending email using MVC

I am not getting any errors currently and am even getting the success message from php mailer but no email is actually being sent.

Here is my email class:

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;

require '../vendor/autoload.php';

class Email {
	
	public function sendMail($email)
	{
	
		$mail = new PHPMailer(true);                              // Passing `true` enables exceptions
		try {
			//Server settings
//			$mail->SMTPDebug = 2;                                 // Enable verbose debug output
			$mail->isSMTP();                                      // Set mailer to use SMTP
			$mail->Host = 'mail.example.com';  // Specify main and backup SMTP servers
			$mail->SMTPAuth = true;                               // Enable SMTP authentication
			$mail->Username = mail@example.com';                 // SMTP username
			$mail->Password = 'secret';                           // SMTP password
			$mail->SMTPSecure = 'tls';                            // Enable TLS encryption, `ssl` also accepted
			$mail->Port = 587;                                    // TCP port to connect to

			//Recipients
			$mail->setFrom('mail@example.com');
			$mail->addAddress($email);     // Add a recipient              // Name is optional
			$mail->addReplyTo('mail@example.com');


			//Content
			$mail->isHTML(true);                                  // Set email format to HTML
			$mail->Subject = 'Forgotten password';
			$mail->Body    = 'This is the HTML message body <b>in bold!</b>';
			$mail->AltBody = 'This is the body in plain text for non-HTML mail clients';

			$mail->send();
		} catch (Exception $e) {
			echo 'Message could not be sent.';
			echo 'Mailer Error: ' . $mail->ErrorInfo;
		}	
	}
}

Open in new window


In my controller I have:

      
public function forgot_password()
	{
		if($_SERVER['REQUEST_METHOD'] == 'POST') {
			
			$email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
			
			$data = [
				
				'email' => $email,
				'message' => ''
			];
			
			if(!filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
				
				$data['message'] .= 'Invalid email address';
			}
			
			if(!$this->userModel->findUserByEmail($email) && filter_var($_POST['email'], FILTER_VALIDATE_EMAIL)) {
				
				$data['message'] .= 'Email address does not exist';
			}
			
			if(!empty($data['message'])) {
				
				$this->view('users/forgot-password', $data);
				
			} else {
				
				$send = new Email();
				$send->sendMail($data['email']);
				$this->view('users/email-sent');
			
			}
			
		} else {
			
			$data = [
				
				'email' => '',
				'message' => ''
			];
			
			$this->view('users/forgot-password', $data);
				
		}	
	}

Open in new window


The email never sends but I can't figure out why.
ASKER CERTIFIED SOLUTION
Avatar of gr8gonzo
gr8gonzo
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of Crazy Horse

ASKER

Thanks gr8gonzo.

I am getting an error from your code labelled NEW:

} catch (Exception $e) {
    throw new \Exception($e->errorMessage()); // Wrap it into a global exception
}
} catch (\Exception $e) { // Global exception - see the leading slash
    throw $e;
}

Open in new window


Parse error: syntax error, unexpected 'catch' (T_CATCH), expecting function (T_FUNCTION)
Sorry - I had a typo. I have too many }. Just remove line 3 so it looks like this:

} catch (Exception $e) {
    throw new \Exception($e->errorMessage()); // Wrap it into a global exception
} catch (\Exception $e) { // Global exception - see the leading slash
    throw $e;
}

Open in new window

Great, thanks. What does the Exception with the \ do?
So, this is weird. If I try send the email to an email address at a different domain I have, I get the success message but never receive any emails. If I change it to my gmail account I get the emails. Any ideas?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Well, you're getting the success message because your code doesn't have any logic to NOT show a success message. You're showing the success view every time, even if the code fails. That's why I was suggesting my change - so your controller can know when something fails and show a different screen.

As far as the different email goes, yeah, the problem is almost guaranteed to be a spam filter issue. There's a variety of things that go into proper email delivery. As the internet continues to fill up with spammers, there's more and more hoops to jump through to prevent people from casually being able to send out emails to (and from) whomever they want.

If I had to guess, you're probably sending email from a machine that doesn't have a PTR / reverse DNS record set up, and probably doesn't use any sort of SPF or DKIM. And if you're on a non-static IP (e.g. a home internet connection or something similar), that will count against you, too. PHPMailer is designed to go straight to the recipient's mail server and try to deliver mail directly, so it gets analyzed more heavily before a successful delivery is allowed.

There's also a smaller chance of a DNS resolution problem. If you're sending to joe@helloworld.com and your server can't resolve helloworld.com to an IP address or can't get the MX records for the helloworld.com domain, then it can't send mail to it.

Check out my article on this topic:
https://www.experts-exchange.com/articles/10089/Trouble-Sending-Mail-from-Your-Script.html

I also have a more in-depth article on email delivery issues if you're still having problems after the above article's suggestions.
https://www.experts-exchange.com/articles/1222/16-Tips-to-Improve-Email-Delivery.html
I have already implemented your new code and still get the success messages about the email being sent (this line gives me all the info)

$mail->SMTPDebug = 3;  

Open in new window


Not sure if it makes a difference, but I am also doing this on localhost/MAMP, not on a live server.

Lastly, would it be better to use something like Mailgun? I looked at it but it seems complicated having to fiddle with the DNS.
On a side note, I should also point out that if you're looking to implement correct MVC structure, just remember that all of your output should be coming from your VIEW and not from libraries or controllers or anything. For example, this is wrong:

} catch (Exception $e) {
    echo 'Message could not be sent.';
    echo 'Mailer Error: ' . $mail->ErrorInfo;
}

...because it's trying to output content directly from the functional code.
Not sure if it makes a difference, but I am also doing this on localhost/MAMP, not on a live server.
Yes, absolutely that makes a difference. You have to look at the world through the eyes of a mail server administrator who is trying to stop spam. Imagine you're getting these messages that:

  1. don't look like NORMAL e-mail messages (they don't have all the headers that are commonly added by email programs),
  2. the messages are not being sent via a trusted mail server,
  3. and the messages are coming from something that is basically anonymous (a dynamic connection without any permanent reverse DNS).

It looks and smells exactly like spam, even if the content itself doesn't really have anything spammy in it. It just has all the characteristics of a message sent by a spammer, so it's going to get blocked.

I don't have any experience with Mailgun, but it sounds like it might work if they have their own mail domain that your emails come from.

Alternatively, if you have a live server that is running SMTP and has a protected relay (which means you can use it to send emails as long as you login first with a username/password), then you can tell PHPMailer to use that server for your SMTP server, and it would also probably resolve the problem.
Thank you!