Symfony2 Exception class, Why use throw new $class? - php

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.

Related

How do I mock an object I can't pass to a method to trigger an exception?

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!

How to structure a simple PHP Exception class

Hi devs
I have a simple PHP OO project. I have an exception who appears 2 times.
Something like :
if (!$x) {
throw new Exception("Don't use this class here.");
}
I want to dev a class in order to edit this code like that :
if (!$x) {
throw new ClassUsageException();
}
How to dev the Excpetion class with default Exception message ?
Thanks
I'd advise creating new exception classes sparsly. It is no fun to check a multitude of exceptions left and right. And if you really feel the need, check what kinds of exceptions are already defined and where in that hierarchy your exception will fit and then extend that class, i.e. give the developers a chance to catch a (meaningful) range of exceptions without having to explicitly write one catch-block after the other.
I'm really not sure what you're trying to achieve here with Don't use this class here. but it could be an InvalidArgumentException (or something derived from that exception, if you really must). There are other mechanisms to prevent an instance of a certain class at a specific place though.
You can extend the Exception class
<?php
Class ClassUsageException extends Exception {
public function __construct($msg = "Don't use this class here.", $code = 0) {
parent::__construct($msg, $code); //Construct the parent thus Exception.
}
}
try {
throw new ClassUsageException();
} catch(ClassUsageException $e) {
echo $e->getMessage(); //Returns Don't use this class here.
}

What is $previous in Exception class?

What does $previous in the Exception constructor argument denote? How could I use it?
class MyException extends \Exception {
private $message;
private $code;
public function __construct($message,$code,\Exception $previous=null){
$this->message = $message;
$this->code = isset($code) ? $code : 0;
parent::__construct($message,$code,$previous);
}
}
I didn't find anything in the API Doc
If you throw an exception because you caught an exception, you can add the original exception as $previous. That means you can actually "nest" exceptions:
try {
throw new FooException('Foo exception');
} catch (FooException $e) {
$code = 1;
throw new BarException('Bar exception', $code, $e);
}
You can then loop over the exception "stack" instead of just the exception caught, providing you with more context.
while($e instanceof \Exception) {
echo $e->getMessage();
$e = $e->getPrevious();
}
Now, you would probably use this if you're implementing a library that can throw an exception, but you'd like to wrap that exception in your own. That way, your clients' code only needs to know of your exceptions, and not that of the exceptions that the dependencies of your code has, while not losing any kind of information.
the reason is that PHP 5.3 introduces nested exceptions as a default part of the PHP base Exception class. While the above code will work, if you are utilizing PHP 5.3, you can pass any previous exception as a third argument , and use the Exception::getPrevious() method to get a previously raised exception.
argument of a previous exception, allowing you to nest the exceptions. When preparing to log your exception, you can opt to iterate through any possible previously thrown and nested exceptions, and log any of the data you need.
What is nesting? Nesting is the ability to catch a particular exception, create a new exception object to be thrown with a reference to the original exception. This then allows the caller access to both the exception thrown from within the consumed library of the more well known type, but also access to the exception that originated this exceptional behavior as well.
Why is this useful? Typically, this is most useful in code that consumes other code that throws exceptions of its own type. This might be code that utilizes the
for more
http://www.brandonsavage.net/exceptional-php-nesting-exceptions-in-php/

How can one extend exception to send emails

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.

How to get called function name in __construct without debug_backtrace

How to get a caller method name in an class constructor, get_called_class() gives me the name of an extended class which was instantiated but how can I get the name of a method which was called in that class?
I need this for a production state so debug_backtrace() is not a good solution.
Why do you need this? If you have any consideration for other coders on the project and standards, find a solution which does not require the constructor to know about how it was called. If all other solutions fail, define a static factory method and make the constructor private for more control over instantiation.
It looks like there is not way to get it without debug_backtrace(). Still you can create your our function for this. Example from manual:
function get_caller_method()
{
$traces = debug_backtrace();
if (isset($traces[2]))
{
return $traces[2]['function'];
}
return null;
}
There is no automatic way of doing this other than the slow/ugly debug_backtrace, or passing the name as an argument to the constructor.... but you shouldn't really be building these kinds of dependencies into your classes.
If you don't want to use the debug_backtrace() function, you can also throw and catch an exception, to get a backtrace.
But that's also ugly... Anyway:
try
{
throw new Exception();
}
catch( Exception $e )
{
print_r( $e->getTrace() );
}
I don't know why you need the caller. Maybe you can fix this by using a different logic.
It should be better than getting a backtrace...
EDIT
The exception does not need to be thrown. Sorry for that part.
So you can simply use:
$e = new Exception();
print_r( $e->getTrace() );
But once again, you shouldn't need to use this!

Categories