Swiftmailer exception doesn't catch in Symfony2 controller - php

I am developing a simple mailing application with Gmail account as host.It works like a charm but the problem rises when send() function throw an exception.
I see that try catch statement can't handle the exception.
It doesn't work even when I use Global exception class.
this question discussed in somewhere also .
for example :
Catch swiftmailer exception in Symfony2 dev env controller
or
https://groups.google.com/forum/#!topic/symfony2/SlEzg_PKwJw
but they didn't reach a working answer.
My controller function code is :
public function ajaxRegisterPublisherAction()
{
//some irrelevant logic
$returns= json_encode(array("username"=>$username,"responseCode"=>$responseCode));
try{
$message = \Swift_Message::newInstance()
->setSubject('hello world')
->setFrom('jafarzadeh91#gmail.com')
->setTo('jafarzadeh991#yahoo.com')
->setBody(
$this->renderView('AcmeSinamelkBundle:Default:email.txt.twig',array('name'=>$username,"password"=>$password))
);
$this->container->get("mailer")->send($message);
}
catch (Exception $e)
{
}
return new \Symfony\Component\HttpFoundation\Response($returns,200,array('Content-Type'=>'application/json'));
}
The response that sent from above code that I receive in firebug console is :
{"username":"xzdvxvvcvxcv","responseCode":200}<!DOCTYPE html>
<html>
.
.
Swift_TransportException: Connection could not be established with host smtp.gmail.com [Unable to find the socket transport "ssl" - did you forget to enable it when you configured PHP?]
.
.
</html>
and I catch my hairs because I don't know why the kernel handle the exception in continue of my json object?
when I comment this line:
$this->container->get("mailer")->send($message);
the exception doesn't occur and I have a valid json in client side.(that is matter-of- course although)
I changed Exception to \Exception or \Swift_TransportException or even Swift_TransportException ! but no good result.

When you do $this->container->get("mailer")->send($message); the email message is not being sent at that point if you have spooling turned on. See http://symfony.com/doc/current/cookbook/email/spool.html
If you have the default setting of spool: { type: memory }, the \Swift_TransportException will be thrown during the kernel termination phase, after your controller has exited.
One way around this is to turn off the spooling (but then your users might have to wait while the email is sent), or you can make your own eventlistener to handle the exception. http://symfony.com/doc/current/cookbook/service_container/event_listener.html

you need to beat the event dispatcher before it send back an exception, so listen to these kind of events and silence them, though i think this is a BAD way to deal with it
class SuchABadRequest implements \Swift_Events_TransportExceptionListener{
/**
* Invoked as a TransportException is thrown in the Transport system.
*
* #param \Swift_Events_TransportExceptionEvent $evt
*/
public function exceptionThrown(\Swift_Events_TransportExceptionEvent $evt)
{
$evt->cancelBubble();
try{
throw $evt->getException();
}catch(\Swift_TransportException $e){
print_r($e->getMessage());
}
}
}
inside the controller
$mailer = $this->get('mailer');
$mailer->registerPlugin(new SuchABadRequest());

Related

How to catch exceptions with symfony without rendering httpException page

I am working on the development of a centralized authentication web app with Symfony 3.4 and I encounter a problem with the auto-management of exception that Symfony provides. The problem is that I want to catch a ConnectionException raised by the LDAP component when the connexion to the Active Directory fails. The objective is to use this exception to notice when it fails and to redirect to a specific page. But at the very moment when the Exception is raised by the LDAP component, Symfony notice a kernel.exception event and then render a debug exception page instead of letting the program go and catch the exception.
How could I do to fix that problem and be sure that the Exception is caught and use by my code and not automatically by Symfony ?
I join you the sample of code and the page rendered:
<?php
namespace App\Utils;
use App\Entity\User;
use Symfony\Component\Ldap\Ldap;
use Symfony\Component\Ldap\Exception\ConnectionException;
class ActiveDirectoryStuff
{
private $domain = 'myDomain';
private $ldapString = 'ldap://';
public function __construct()
{}
public function connection($username, $password)
{
try {
$dn = $username.'#'.$this->domain;
$ldap = Ldap::create('ext_ldap', array(
'connection_string' => $this->ldapString . $this->domain,
));
$ldap->bind($dn, $password); //where the ConnectionException is raised
return $ldap;
} catch (\ConnectionException $ce) {
throw $ce;
}
}
}
Image of the Page Rendered Automatically
You're catching the exception, doing nothing then throwing, this is the same as having no try catch at this level of code
Based on what you said use your framework to redirect to a specific page from within the catch and any other logic you want to run when this error happens, then remove throw $ce; that will stop the default error handler from running which I assume is Symfony's

Symfony2 catch PDO errors when booting Kernel

I am using Symfony 2.4 and am trying to create a more powerful exceptions handler that, on certain PDO / Doctrine exceptions, changes the status code of the response from 500 to 503 to display a different custom error message than our standard (in other words, it returns the error503.html.twig template rather than error500.html.twig). So far, I have created a custom Exceptions controller that extends the TwigBundle ExceptionController, I have changed the Twig exception parameter in config.yml, and I am able to catch any and all exceptions that are thrown once Symfony calls handle(...) in HttpKernel.php:185 (so it's really the second time that handle is called -- this time being on the HttpKernel rather than the AppKernel). I'll refrain from posting all that code, and instead direct the reader here to learn more about my method if they are unfamiliar. All of that code is working just fine -- I am able to modify any applications that are thrown within my application, so you can assume that I'm using the aforementioned approach properly.
The issue I am running into is that in addition to catching exceptions that are thrown within Symfony, I also want to also be able to catch exceptions that are thrown before the HttpKernel's handle method is called (an example being a PDO Access Denied exception that is thrown from improper database credentials). To give you a more specific rundown, in app_dev.php, you have:
$response = $kernel->handle($request);
which calls:
/**
* {#inheritdoc}
*
* #api
*/
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
if (false === $this->booted) {
$this->boot();
}
return $this->getHttpKernel()->handle($request, $type, $catch);
}
Now, if an exception gets thrown in $this->boot(), it doesn't look like it gets caught anywhere, and because of that, I can't see any way of gracefully handling said exception in Symfony. It's only if the exception gets thrown within the try / catch block contained in $this->getHttpKernel()->handle($request, $type, $catch) that it will be caught and gracefully handled using Symfony code. Am I wrong about that? Does anyone know of an approach to handling exceptions that are thrown in this context that utilizes Symfony? My apologies in advance if this has already been answered elsewhere.
I ran into a similar problem, I didn't see a neat way around this but was able to get nice error pages for my specific problem simply by generating a Response object and sending that. I placed the following in some code which was called by boot()
try {
someExceptionFunction();
} catch (Exception $e) {
$response = new Response('<html><body>'.$e->getMessage().'</body></html>');
$response->send();
exit;
}
You could easily add some more logic to the catch block, catching different exceptions. It's not as clean/abstract as it could be, but since the entire framework fails to boot I don't know of any option you could use it to parse an error page.
Hope this helps

Cannot catch exceptions in Module.php

I am having an issue where an exception is being thrown but not caught in Module.php. The code that throws the exception is wrapped in a try/catch. There's nothing really fancy going on so I am assuming ZF2 has a quirk and/or reason for not allowing exceptions to be caught here.
The code is as follows (simplified as necessary):
class Module
{
public function onBootstrap(MvcEvent $e)
{
// Get service manager, etc.
// Set up some environment stuff
try {
// exception is thrown
}
catch (\Exception $e) {
echo $e->get_message();
exit;
}
}
}
Why is the exception not being caught?
In an ideal world there would be a way to catch this exception here. But if that is not possible, or is too convoluted to be worth the effort, an alternative process to fetching this data (regardless of source) as early in the page loading process as possible would be appreciated.
meta
I know the code in Module.php is supposed to be lightweight. But we have to fetch some data immediately before any action is performed as it will contain data vital to those actions to be performed. This data is cached after the first visit so every other page load will avoid this overhead.
I also have Googled this but apparently no one else has either experienced this, asked about it, or documented it.
This code works just fine for me in a module class:
public function onBootstrap(MvcEvent $e)
{
try {
// exception is thrown
throw new \Exception('My exception here');
}
catch (\Exception $e) {
echo $e->getMessage();
exit;
}
}
It displays the exception message and exits.
One way to investigate what is happening is to use xdebug for step by step debugging.
Just add a breakpoint in your Module and see what ZF is doing.

swiftmailer Swift_TransportException gets uncaught by try-catch block

For a project (which is about estate properties) i have included a contact form where a visitor can contact an estate broker if the visitor is interested to buy/hire an estate property.
I am using Symfony2 and its library. For the contact mail, i am using the Swiftmailer library.
Well, i have the next code which handles the form submit. there, I create a mail object to be able to send mails. It works, but i want to provide a error resolving service if there are problems with the smtp host from sender/receiver.
Here is the code,
$data = $contactForm->getData();
try {
// create body text
$body = $app['twig']->render('mailTemplate.twig', array('data' => $data, 'immoid' => $immoID));
// set mail
$mail = \Swift_Message::newInstance()
->setSubject('Contact reaction on your immo offer.')
->setFrom($app['swiftconfig']['sender'])
->setTo($contactinfo['contactmail'])
->setBody($body, 'text/html');
// send mail
$app['mailer']->send($mail);
// redirect if successfull
$app->redirect($app['url_generator']->generate('immoDetail', array('immoID' => $immoID)));
}
catch (Swift_TransportException $STe) {
// logging error
$string = date("Y-m-d H:i:s") . ' - ' . $STe->getMessage() . PHP_EOL;
file_put_contents("errorlog.txt", $string, FILE_APPEND);
// send error note to user
$errorMsg = "the mail service has encountered a problem. Please retry later or contact the site admin.";
}
catch (Exception $e) {
// logging error
$string = date("Y-m-d H:i:s") . ' - GENERAL ERROR - ' . $e->getMessage() . PHP_EOL;
file_put_contents("errorlog.txt", $string, FILE_APPEND);
// redirect to error page
$app->abort(500, "Oops, something went seriously wrong. Please retry later !");
}
($app['swiftconfig']['sender'] = mailaddress from host / $contactinfo['contactmail'] = mailaddress from site visitor (submitted in contact form))
Now, when the smtp host doesn't work, Swiftmailer DOES send an exception, but the try-catch block ISN'T catching it. The function is just being continued.
Even the root try-catch block (in app.php) isn't catching it too. As a result of this, you see a large PHP error on the webpage, which shouldn't happen. The message from it is described here below,
SCREAM: Error suppression ignored for
---
Fatal error: Uncaught exception 'Swift_TransportException' with message ' in C:\...\vendor\swiftmailer\swiftmailer\lib\classes\Swift\Transport\StreamBuffer.php on line 266
---
Swift_TransportException: Connection could not be established with host <output omitted>
Does anyone know why the try catch block isn't catching the custom exception ? I have investigated the class files and the progress, but i don't see any unusual activity.
I hope that someone can come with a solution to this because PHP errors shouldn't appear on site pages.
remove from your config.yml
spool: { type: memory }
Then you will be to add try catch like this
try{
$mailer = $this->getMailer();
$response = $this->getMailer()->send($message);
}catch(\Swift_TransportException $e){
$response = $e->getMessage() ;
}
Try to put a backslash infront of Exception.
catch(\Exception $e)
A \ (backslash) before the beginning of a class name represents the Global Namespace.
Using a backslash will ensure that the Exception is called within the global namespace.
When you do $app['mailer']->send($mail); the email message is not being sent at that point if you have spooling turned on. See http://symfony.com/doc/current/cookbook/email/spool.html
If you have the default setting of spool: { type: memory }, the \Swift_TransportException will be thrown during the kernel termination phase, after you controller has exited.
One way around this is to turn off the spooling (but then your users might have to wait while the email is sent), or you can make your own eventlistener to handle the exception. http://symfony.com/doc/current/cookbook/service_container/event_listener.html
I ran into this problem in Laravel 4.2 when I added a SwiftMailerHandler to Monolog so it would email to me anything logged at a certain level or above. There was no way for me to catch the exception in the handler and it was thrown back to the browser. I solved it by subclassing SwiftMailerHandler:
<?php
/*******************************************************************************
* $Id: LogMailer.php 12152 2015-09-15 00:42:38Z sthames $ */
/**
* Subclass of Monolog SwiftMailerHandler that will catch exceptions SwiftMailer
* throws during the send. These exceptions are logged at the critical level.
* Without this, such exceptions are reported back to the browswer.
*******************************************************************************/
class LogMailer extends \Monolog\Handler\SwiftMailerHandler
{
/** Flag set when logging an exception during send. */
protected $dont_mail = false;
/** Overloads sender to catch and log errors during the send. */
protected function send($content, array $records)
{
try
{
if (!$this->dont_mail)
parent::send($content, $records);
}
catch(\Exception $e)
{
$this->dont_mail = true;
Log::critical($e->getMessage());
$this->dont_mail = false;
}
}
}
This handler catches the SwiftMailer exception and logs it at the critical level so it's not lost. I don't get an email about it but at least it's in the log.
I was surprised to find SwiftMailerHandler offered no way to inject an exception handler into the send method but fortunately the code is written well enough to make this solution pretty simple.
Works great and has given me no trouble so far.
Sure you have the latest version of swiftmailer? Line 226 of StreamBuffer.php doesn't throw any exception. This version from 2 years ago does throw the exception on that specific line.
I'd try running the latest version before diving into the issue more deeply.
SCREAM is a setting of XDebug that allows to see errors that are normally caught or suppressed. (Using #, for example.) Search for
xdebug.scream = 1
in your php.ini and set this to 0.
you can try below code for custom error:
public function render($request, Exception $exception)
{
if ($exception instanceof \Swift_TransportException) {
return response()->view('errors.404');
}
return parent::render($request, $exception);
}

How to handle a Beanstalkd job that has an error

When my Beanstalkd job has an error, like "exception 'ErrorException' with message 'Notice: Undefined index: id in /var/www/mysite/app/libraries/lib.php line 248' in /var/www/mysite/app/libraries/lib.php:248, how should Beanstalkd knows that an error has occured and mark it as failed so it can be retried again?
Install a monitor for beanstalkd which can be useful when developing/testing your app. Some alternatives that uses PHP: http://mnapoli.github.io/phpBeanstalkdAdmin/ and https://github.com/ptrofimov/beanstalk_console
As for handling the errors, you could define your own error handler for beanstalkd jobs and in that handler decide if you want to:
bury (put the job aside for later inspection)
kick (put it back into the queue)
delete (remove it, if this is safe for your application)
EDIT - Did you manage to solve your problem?
The best way might be to use a try/catch around your jobs to catch the exceptions, and then bury it if the exception is raised at the worker. If the exception is raised at the producer it is probably never added into the queue so no need to bury() then, but use a monitor to make sure.
If you want to try to define an own error handler for your object I´ve done something similar before by setting up a custom error handler for your class. It might be a ugly hack by trying to get the pheanstalk (job) object through $errcontext - but might be something to try.. Here is some pseudo-code I quickly put together if you want to try it out (created a subclass to avoid put code into pheanstalk class):
class MyPheanstalk extends Pheanstalk {
function __construct() {
//register your custom error_handler for objects of this class
set_error_handler(array($this, 'myPheanstalk_error_handler'));
//call parent constructor
parent::__construct();
}
function myPheanstalk_error_handler($errno, $errstr, $errfile, $errline, $errcontext) {
// get the current job that failed
foreach($errcontext as $val) //print_r($errcontext) to find info on the object(job) you are looking for
{
if(is_object($val)) {
if(get_class($val) == 'Pheanstalk') { //and replace with correct class here
//optionally check errstr to decide if you want to delete() or kick() instead of bury()
$this->bury($val);
}
}
}
}
}
Its your script that has the error, not beanstalkd.
try {
//your code from Line 248 here
} catch (ErrorException $e) { //this section catches the error.
//you can print the error
echo 'An error occurred'. $e->getMessage();
//or do something else like try reporting the ID is bad.
if (!isset($id)) {
echo 'The id was not set!';
}
} //end of try/catch

Categories