I have a PHP script which needs to execute a callback function and catch a specific type of error.
<?php
class MyException extends Exception{}
function doStuff(callable $callback){
try{
$callback();
}
catch(MyException $e){
#Deal with it
}
}
?>
The callback function can use a try-catch which will prevent my exception being bubbled up. How can I allow the callback function to implement try-catch blocks but not allow it to catch MyException?
Forgive me if I've read the question wrong - I can not think of a use case where this is required - but I think something like this will work:
<?php
class MyException extends Exception {}
/*
* This method is in the library. It's not in your control
*/
function doStuff(callable $callback) {
try {
$callback();
} catch(Exception $e) {
die($e->getMessage());
}
}
/*
* We want to do something, but not let any MyException pass through to doStuff()
*/
doStuff(function() {
try {
connect('mysql:dbname=IDoNotExist;host=127.0.0.1', 'batman', 'robin');
} catch(MyException $e) {
return; // Do nothing with MyException, continue as if it didn't happen
} catch(Exception $e) {
throw new Exception($e); // Not an instance of MyException ... let the library handle it
}
});
/*
* This is dummy code that will throw MyException if the username and password are incorrect
*/
function connect($dsn, $username, $password) {
try {
$dbh = new PDO($dsn, $username, $password);
} catch(PDOException $e) {
throw new MyException($e->getMessage());
}
}
I'm assuming, by reading the comments and your responses, that doStuff() is some library call that you pass a callable to. If the code inside your callback catches a MyException you want to completely ignore this.
The code above will completely ignore MyException as if nothing happened. Although I would never advocate such a solution.
Related
I have a class named User that calls one of its own methods, setUsername(), upon construction, within a try/catch block. If setUsername() fails, it will throw an exception:
class User {
private $username;
public function __construct($input_username) {
try {
$this->setUsername($input_username);
} catch(Exception $e) {
throw $e;
}
}
private function setUsername($username) {
if(1 != 0)
throw new Exception("1 does not equal 0!!!");
$this->username = $username;
}
}
I then create a new User in an external function, in a separate file, within its own try/catch block. It's supposed to catch the exception passed through from the User class constructor:
namespace UserController;
function createUser(){
try {
$user = new \User('sample-user');
} catch(Exception $e) {
echo $e->getMessage();
}
}
Why, then, am I still getting an "Uncaught Exception" error?
It seems I was missing a statement at the top of the file that instantiates the class, since it is namespaced. After the namespace declaration, it needs to say:
use \Exception;
For example:
class Test{
public function doStuff(){
throw new Exception("Something gone wrong...");
}
}
class TestFactory{
public static function getTest(){
try{
$testObj = new Test();
return $testObj;
}catch(Exception $e){
//... handle $e ...
throw new Exception("Exception from factory");
}
}
}
When I call "doStuff()", its possible to throw "Exception from factory" instead of "Something gone wrong..." ?
$obj = TestFactory::getTest();
$obj->doStuff(); //Called outside TestFactory
No, try catches errors only within its scope. In your example it will catch exceptions in the Test::__construct only. As soon as object is returned from the factory, it left the try/catch scope.
You can handle "Something gone wrong..." this way:
try {
$obj = TestFactory::getTest();
$obj->doStuff(); //Called outside TestFactory
} catch(Exception $e) {
//... handle $e ...
}
As mentioned by Alex, it is not possible to catch exceptions in the factory method, because when the exception occurs, the factory method is long finished.
From op's comment
The case is I'm extending Pdo and PdoStatement, that throws PdoException in methods like "execute", "prepare" , etc... I have my CustomException class that log those errors and i would like to throw it instead of PdoException, whenever its thrown
PDO will by itself always throw PDOExceptions. I don't think we should change PDO code in a way that it throws other types of Exceptions, nor should we ever throw PDOExceptions ourselves.
What we can do however, is catch those PDOExceptions and throw our CustomExceptions on top of it:
public function doStuff()
{
try { /* PDO code that can throw PDOException */ }
catch (\PDOException $ex)
{
throw new CustomPDOException("Something gone wrong", $ex->getCode(), $ex);
}
}
You have to wrap your code in a way to catch all errors that can be thrown by PDO.
I'm trying to test a capturing and handling a custom exception in PHP.
I've extended the base exception type with some extra properties and methods.
One of the classes I'm stubbing can throw an exception, I want to be able to test that I'm correctly capturing and handling that exception (which in this case means building a response object to return from the call).
e.g.
try {
$objectBeingStubbed->doSomething();
} catch (\Exception $ex) {
if ($ex instanceof CustomExceptionType) {
$this->_errorResponse->error->message = $exception->getMessage();
$this->_errorResponse->error->code = $exception->getCode();
$this->_errorResponse->error->data = $exception->getData();
} else {
throw $ex;
}
}
I'm attempted to simulate the exception being thrown with:
$objectStub->expects($this->any())
->method('doSomething')
->will($this->throwException(new CustomExceptionType()));
But when the exception arrives in the class I'm testing it's now an instance of "Mock_ErrorResponse_????" which doesn't extend my custom exception. My exception is instead contained in a "$exception" property on the Mock_ErrorResponse.
Is there any way of handling this without being forced to do something horrible like:
if ($ex instanceof PHPUnit_Framework_MockObject_Stub_Exception) {
$ex = $ex->exception;
}
if ($ex instanceof CustomExceptionType) {
...
Inside the class I'm testing?
First of all, instead:
} catch (\Exception $ex) {
if ($ex instanceof CustomExceptionType) {
you should use try/catch structure:
// (...)
} catch (CustomExceptionType $e) {
// (...)
} catch (\Exception $e) {
// (...)
}
So, answering your question, basically probably you're doing sth wrong. Because when the stubbed method throws an exception, it should throw exactly exception that you've set with throwException method.
I don't know how you build your stub (maybe there something is broken, maybe namespaces) but please consider an example below which works fine.
class Unit
{
public function foo()
{
throw new \InvalidArgumentException();
}
public function bar()
{
try {
$this->foo();
} catch (\InvalidArgumentException $e) {
return true;
} catch (\Exception $e) {
return false;
}
return false;
}
}
class UnitTest extends \PHPUnit_Framework_TestCase
{
public function testBar()
{
$sut = $this->getMock('Unit', array('foo'));
$sut->expects($this->any())
->method('foo')
->will($this->throwException(new \InvalidArgumentException()));
$this->assertTrue($sut->bar());
}
}
Of course you can replace InvalidArgumentException with your own implementation exception and this still should work. If you'll still have problems with figure out what is wrong with your code please post more complete example (eg. how you build your stub). Maybe then I can help more.
Nowadays you can use the #expectedException php-doc annotation built-in in PHPUnit: https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.exceptions
/**
* #expectedException InvalidArgumentException
*/
public function testBar()
{
$sut = $this->getMock('Unit', array('foo'));
$sut->expects($this->any())
->method('foo')
->will($this->throwException(new \InvalidArgumentException()));
}
How to prevent further execution of class if something fails in constructor.
........Worker.php..............
class Worker {
public function __construct() {
try {
$this->pheanstalk = new Pheanstalk('127.0.0.1');
}
catch (Exception $e) {
logFatal('Pheanstalk: '.$e->getMessage());
}
}
.............
.............
.............
.............
}
.
............processing.php..........
require_once ROOTPATH.'worker.php';
$worker = new worker();
$worker -> put($Data);
.............
.............
.............
.............
Now if the try block fails in the constructor i dont want to execute put() but rest of code should continue in processing.php
new Pheanstalk('127.0.0.1'); throws a exception which is caught by catch.
Best solution is to catch the exception outside your class. Not only can you skip the put, logging errors is also not really the responsibility of that class anyway. Oh and Unit testing is easier too!
class SomeClass
{
public function __construct()
{
if ($somethingFails === true)
throw new Exception();
}
}
try {
$instance = new SomeClass();
$instance->put();
} catch (Exception $exception) {
// Handle here
logFatal('Pheanstalk: '.$e->getMessage());
}
If it's another piece of application throwing the exception, and your constructor is wrapped around it. Consider catching the exception, and then throwing your own.
Why don't you throw an exception in the constructor?
See http://php.net/manual/en/language.exceptions.php
Is this approach ok? Am I handling exceptions correctly? See my class:
class Email extends String
{
protected function validate($email)
{
try{
parent::validate($email);
} catch(InvalidArgumentException $e) {
throw $e;
}
if(!filter_var($value,FILTER_VALIDATE_EMAIL))
{
throw new InvalidArgumentException('etc.');
}
}
}
If you are not going to do anything with the exception within that catch block, it's not necessary to enclose that parent method call in its own try-catch block. The method will automatically pass the exception up from the parent's implementation if it encounters one outside a try-catch block, just like if you threw an exception from the same context (as you do after your if condition):
protected function validate($email)
{
parent::validate($email);
if (!filter_var($value, FILTER_VALIDATE_EMAIL))
{
throw new InvalidArgumentException('etc.');
}
}