This user asked a similar question but had no viable solution in the answers, so here is am revisiting the topic.
I would like to have emails sent from custom exceptions in a clean way. I can do this easy with plain old errors from a custom error_handler, but with extended exceptions I am a bit stumped.
The main problem is I rely on Zend_Mail to have transparent cross-platform mailing, easy smtp/ssl configuration for gmail account usage and a bunch of other goodies. I would like to access my Zend_Mail object in a custom exception. Static calls are not really an option that I am willing to use, although it seems to be the easiest to use a 3rd party object in a custom exception. Here is the constructer of base exception class:
public function __construct($message = null, $code = 0,
Exception $previous = null);
The only solution I think may be worthwhile at the time being is adding a parameter for mailer object in the extended exception class, yet I do not want to require all subclasses to pass in mailer object. The idea is that maybe this mailer obj. would be better off being an optional dependency.
__construct signature would then become:
public function __construct($mailobj = null, $message = null, $code = 0,
Exception $previous = null);
Notice the order of parameters, only $code is required and it is in the middle of the signature! This poses another issue but that is for another day. My question for today is, does anyone have ideas / suggestions on how to deal with dependencies in custom exception classes?
Keep in mind we are aiming for testable code.
Another easy option would have been to have the mailing code in a catch block, but that kind of defeats the purpose of using custom exceptions in my opinion, because the client of this class now has to think of adding mailing code in all catch blocks.
I would approach it like this (am I missing some requirement here?)
class CustomException extends Exception // Zend_Exception?
{
public function __construct($message = null, $code = 0, Exception $previous = null) {
$mailobj = new Zend_Mail(/* ... */);
// ....
try { // In order not to get infinitelly looped
$mailobj->send(/*...*/);
} catch(Exception $e) { }
}
}
Whenever this exception is instantiated (i.e. throw new CustomException() ), someone will be receiving an email.
Related
I develop a pretty big web application using laravel. Logging to a single file or daily files seems too messy for me.
My purpose is to create error logs which will be categorised and easy for retrieval to show on front-end for tech support. E.g. user entered wrong api-key, email, whatever.
P.S. Users don't always understand meaning of the error showed so they contact tech support.
Example for better understanding:
Suppose I have model relations: User->hasMany->Project and in some
project appears an exception I want it to be written and related to
the project. e.t.c. So I could do something like $some_project_model->logs()->get() and tech support got all logs related to
certain project.
What I have in my mind is separate table for logs which is related to certain Eloquent models. But to do so ExceptionHandler needs to know to which model an Exception is related to.
So how this can be implemented? Maybe I could assign custom ExceptionHandler to certain class?
Thank you for any suggestions.
So I've actually found a proper solution:
First of all it's needed to create Log table with polymorphic relation. Then to catch all the exceptions in certain context __call magic method can be used. __call method allows to wrap every object method call with custom code. Try-catch in this case.
Example:
class Loggable
{
public function __call($method, $arguments)
{
try {
return call_user_func_array([$this->certain_obj, $method], $arguments);
} catch (Exception $e) {
//creating new $log orm
$this->get_orm()->loggable()->save($log);
}
}
protected function do_something() {
//something is going on
throw new \Exception();
}
}
$loggable_obj = new Loggable();
$loggable_obj->do_something();
To make it work, you must make all loggable methods private or protected.
In case if you think that 'encapsulation' is not just another difficult word you can achieve the same result using proxy class.
Example:
class Proxy
{
private $some_obj;
public function __construct($some_obj)
{
$this->some_obj = $some_obj;
}
public function __call($method, $arguments)
{
try {
return call_user_func_array([$this->some_obj, $method], $arguments);
} catch (Exception $e) {
//creating new $log orm
$this->some_obj->get_orm()->loggable()->save($log);
}
}
}
$proxy = new Proxy($some_obj);
$proxy->whatever_method($foo, $bar);
So, in both cases I can get all logs related to some exact orm by calling ->logs()->get() on it.
Not a rocket science at all.
One approach might be to create your own custom exception (sub)class which you could add model and/or project information to at the point of throwing the exception. Then this information would be available in the ExceptionHandler.
You wouldn't have it for built in or third-party exception types though. For those you'd end up having to catch and rethrow where possible (and necessary), wrapping in your custom exception class, which probably isn't ideal.
Consider the following method:
function m1()
{
$ent = new Entity;
...
try {
$ent->save();
} catch (QueryException $e) {
...
}
I've got to trigger an exception. Preferably with mockery. How do I do that?
P.S. I can't pass $ent into the method.
UPD Let me describe my particular case to confirm if I do need to trigger an exception. Here I'm trying to test controller's action that is triggered by payment system to notify that user has made a payment. In it I, among other things, store in database all the data coming from payment system in PaymentSystemCallback model, and link it to Order model, which is created before redirecting user to the payment system. So, it goes like this:
function callback(Request $request)
{
$c = new PaymentSystemCallback;
$c->remote_addr = $request->ip();
$c->post_data = ...;
$c->headers = ...;
...
$c->save();
$c->order_id = $request->request->get('order_id');
$c->save();
}
But if incorrect order_id comes in, foreign constraint fails, so I change it this way:
try {
$c->save();
} catch (QueryException $e) {
return response('', 400);
}
But it doesn't look good to handle any database exception this way, so I'm seeking for a way to rethrow the exception unless $e->errorInfo[1] == 1452.
And here's what I came up with:
/**
* #runInSeparateProcess
* #preserveGlobalState disabled
*/
function testExceptionOnSave()
{
$this->setUpState();
Mockery::mock('overload:App\PaymentSystemCallback')
->shouldReceive('save')
->andReturnUsing(function() {}, function() {
throw new QueryException('', [], new Exception);
});
$this->doRequest();
$this->assertBalanceDidntChange();
$this->assertNotProcessed();
$this->seeStatusCode(500);
}
I use #runInSeparateProcess because preceding tests trigger the same action, and therefore the class is loaded before mockery has a chance to mock it.
As for #preserveGlobalState disabled it doesn't work without it. As phpunit's documentation put it:
Note: By default, PHPUnit will attempt to preserve the global state from the parent process by serializing all globals in the parent process and unserializing them in the child process. This can cause problems if the parent process contains globals that are not serializable. See the section called “#preserveGlobalState” for information on how to fix this.
I deviate a little from what mockery's documentation says when I'm marking only one test to run in a separate process, since I need it only for one test. Not the whole class.
Constrictive criticism is welcome.
The easiest way around this is to call a factory method that creates a mock instance of your Entity. Something like:
function testSomething()
{
$ent = $this->getEntity();
...
try {
$ent->save();
} catch (QueryException $e) {
...
}
}
function getEntity()
{
$mock = $this->createMock(Entity::class);
$mock
->method('save')
->will($this->throwException(new QueryException));
return $mock;
}
Your method is not designed for test. Fix that. If you can't, then you have to monkey patch, which PHP does not support natively.
My recommended approach would be to have your test suite install its own priority autoloader. Have your test case register a mock class into that autoloader, associated with class name Entity. Your mock class will do its magic to throw an exception. If you're using PHP 7, you have access to anonymous classes, which makes fixtures easier: new class Entity {}.
Per the accepted answer, Mockery supports this autoloading trick using the overload: quantifier on mocked classes. This saves a lot of work on your part!
i am thinking hard about how to implement a good plugin-system in php. I read a lot about hooking systems and different ways to implement such things, but until now I did not find any approach or existing system that would fit my needs.
I want to have an object (let it be $plugins) in which all used plugins are available as sub-object ($plugin->$blog). So far, thats no problem. But how could I catch a non existant plugin?
If somewhere in the code I would call the plugin $blog by using $plugin->$blog->loadArticle or something similar and the plugin was not loaded by the plugin system, a fatal error would be thrown. Is it possible to catch and handle these errors within a debugging class?
Thanks for your thoughts! Please excuse my english... ;-)
Well, you can implement your Plugin object like this using magic __get()
class Plugins {
private $plugins = array();
public function __get($name) {
if (!isset($this->plugins[$name]) {
throw new PluginNotInstalledException(); //this can be catched with try / catch
}
return $this->plugins[$name];
}
... //methods addPlugin() etc..
}
Whenever you're setting your $blog property would be a good time to check whether the passed parameter is === NULL and throw an exception if that's the case. So you'll be sure that $plugin->blog isn't null.
It would be even better if your plugins have to implement an interface or inherit an abstract class. So then whenever you're setting a property you can check whether the passed parameter is instanceof that class/interface.
A simple approach would be to provide a getPlugin() method.
function getPlugin($plugin)
{
if(! isset($this->$plugin)) return false;
return $this->$plugin;
}
$blog = $plugins->getPlugin('blog');
if($blog === false)
{//error condition
}
I'm looking at Symfony2 classes and I found something like this
try {
return $this->parseSelectorGroup($stream);
} catch (\Exception $e) {
$class = get_class($e);
throw new $class(sprintf('%s at %s -> %s', $e->getMessage(), implode($stream->getUsed(), ''), $stream->peek()), 0, $e);
}
Why they use
$class = get_class($e);
throw new $class
such syntax. What is the purpose of that?
I'm not the original author of that line of code so I can only assume why this was done.
As the code shows, the standard message of the exception is being extended with additional information of the string handling class (getUsed, peek - sounds like a stream buffer/parser/scanner/peeker).
The Message is edited and then a new object of the same class is being thrown.
Generally: When you have a class name in a variable and want to create a new instance of that class, you can simply use:
$className = "ClassName";
$instance = new $className();
This is done here with the catched exceptions classname, see get_class.
Further Note: This code is conceptually broken because it uses a class with an undefined interface for it's constructor. It then makes use of the constructor function without knowing anything about it's constructor.
There might be some information hidden that such a case is not to be expected to happen though. So this is merely a further note. You should have concrete reasons to do something like that, so this has been done for a reason and there might be some design decision which is not documented by the some lines of code you've posted. So handle with care.
Working on a symfony application that uses nusoap (is this the best method for integrating soap work with php/symfony?) for taking credit card payments.
I've simplified an example of my code below.
What I'm struggling with is the best way to handle exceptions. The example below only has 1 custom exception (where should my custom exceptions reside within the directory structure of symfony? (lib/exception?)) But what happens when there are several different types of exceptions that handle a specific error? It's not very elegant to have a try/catch block with 20 odd exceptions.
I'm also not sure of where I should be throwing and catching. I need to set some user flashes to alert the user of any problems, so I figure the catching should be done in the actions controller rather than within the class that handles the soap call.
Could anyone please advise where I might be going wrong?
I hate messy code/solutions and want to stick to the DRY principle as much as possible. I think I might also be missing some built in symfony functionality that might help with this but whenever I search I usually find examples that are for symfony 1.2, I'm using 1.4.
Some examples would be great, thanks.
lib/soap_payment.class.php
class SoapPayment
{
public function charge()
{
/*assume options are setup correctly for sake of example*/
try
{
$this->call();
}
catch (SoapPaymentClientFaultException $e)
{
/* should this be caught here? */
}
}
private function call()
{
$this->client->call($this->options);
if ($this->client->hasFault())
{
throw new SoapPaymentClientFaultException();
}
}
}
apps/frontend/payment/actions/actions.class.php
class paymentActions extends sfActions
{
public function executeCreate(sfWebRequest $request)
{
/* check form is valid etc */
$soap_payment = new SoapPayment();
try
{
$soap_payment->charge();
}
catch (SoapPaymentClientFaultException $e)
{
/* or throw/catch here? */
$this->getUser()->setFlash('error', ...);
$this->getLogger()->err(...);
}
/* save form regardless, will set a flag to check if successful or not in try/catch block */
}
}
One not very well known feature of Symfony is that exceptions can manage the content sent in a response. So you could do something like this:
class SoapException extends sfException
{
public function printStackTrace() //called by sfFrontWebController when an sfException is thrown
{
$response = sfContext::getInstance()->getResponse();
if (null === $response)
{
$response = new sfWebResponse(sfContext::getInstance()->getEventDispatcher());
sfContext::getInstance()->setResponse($response);
}
$response->setStatusCode(5xx);
$response->setContent('oh noes'); //probably you want a whole template here that prints the message that was a part of the SoapException
}
}
If you need a cleaner handling of SOAP exceptions, like setting flashes, etc. you'll probably have to catch each exception. One idea here might be to create a generic SoapException class that is extended by more specific SoapExceptions so you don't have to catch a bunch of different types. The above code may be a useful fallback mechanism as well.
Finally, yes, you should place custom exceptions in lib/exception.