Why is my exception thrown from a closure not caught? - php

I've written a PHPUnit test that checks if an exception is thrown from a closure when a method is invoked. The closure function is passed in as an argument to the method with an exception being thrown from it.
public function testExceptionThrownFromClosure()
{
try {
$this->_externalResourceTemplate->get(
$this->_expectedUrl,
$this->_paramsOne,
function ($anything) {
throw new Some_Exception('message');
}
);
$this->fail("Expected exception has not been found");
} catch (Some_Exception $e) {
var_dump($e->getMessage()); die;
}
}
The code for the get function specified on the ExternalResourceTemplate is
public function get($url, $params, $closure)
{
try {
$this->_getHttpClient()->setUri($url);
foreach ($params as $key => $value) {
$this->_getHttpClient()->setParameterGet($key, $value);
}
$response = $this->_getHttpClient()->request();
return $closure($response->getBody());
} catch (Exception $e) {
//Log
//Monitor
}
}
Any ideas why the fail assert statement is called? Can you not catch exceptions thrown from closures in PHP or is there a specific way of dealing with them I don't know about.
For me the exception should just propagate out the return stack, but it doesn't appear to. Is this a bug? FYI I'm running PHP 5.3.3

Thanks for the answers...
Managed to figure out the issue. It looks like the problem is that the try-catch block that's being invoked is the one where the closure is invoked. Which makes sense...
So the code above should be
public function get($url, $params, $closure)
{
try {
$this->_getHttpClient()->setUri($url);
foreach ($params as $key => $value) {
$this->_getHttpClient()->setParameterGet($key, $value);
}
$response = $this->_getHttpClient()->request();
return $closure($response->getBody());
} catch (Exception $e) {
//Log
//Monitor
throw new Some_Specific_Exception("Exception is actually caught here");
}
}
So it looks like PHP 5.3.3 doesn't have a bug after all which was mentioned. My mistake.

I cannot reproduce the behavior, my example script
<?php
class Some_Exception extends Exception { }
echo 'php ', phpversion(), "\n";
$foo = new Foo;
$foo->testExceptionThrownFromClosure();
class Foo {
public function __construct() {
$this->_externalResourceTemplate = new Bar();
$this->_expectedUrl = '_expectedUrl';
$this->_paramsOne = '_paramsOne';
}
public function testExceptionThrownFromClosure()
{
try {
$this->_externalResourceTemplate->get(
$this->_expectedUrl,
$this->_paramsOne,
function ($anything) {
throw new Some_Exception('message');
}
);
$this->fail("Expected exception has not been found");
} catch (Some_Exception $e) {
var_dump('my exception handler', $e->getMessage()); die;
}
}
}
class Bar {
public function get($url, $p, $fn) {
$fn(1);
}
}
prints
php 5.4.7
string(20) "my exception handler"
string(7) "message"
as expected

Related

Variable/Dynamic Exception Type in Catch Block

I am trying to create a function that when called is passed a callable and an exception "type", either as a ::class string or something like that. The idea would be that the function would call the callback inside of a try/catch block.
Sample Function:
public static function try(callable $callback, string $exceptionClass) : object | null {
try {
...
$callback();
...
catch($exceptionClass $e) {
...
}
}
I found this answer. But I'm not sure how to get the type.
You can't use a variable in the catch like this:
try {
$callback();
} catch ($exceptionClass $e) {
}
You'll have to do something like this, where you catch everything, and then conditionally check the exception type:
try {
$callback();
} catch (\Throwable $e) {
if ($e instanceof $exceptionClass) {
}
}

Laravel create custom exception with parameters & return as JSON

I want to throw a custom exception if the variable is null and return it as JSON.
I've tried like this:
Controller
try {
$check_api_key = $attendance_libraries->check_api_key($this->request);
if ($check_api_key == null) {
throw new NullException(false, 'API Key Not Found', null, 500);
}
} catch (NullException $e) {
return $e;
} catch (\Exception $e) {
// do something else
}
Custom Exception
<?php
namespace App\Exceptions;
use Exception;
use Illuminate\Http\Response;
class NullException extends Exception
{
public $is_success;
public $message;
public $code;
public $data;
public function __construct($is_success, $message, $code, $data = null, Exception $previous = NULL)
{
$this->is_success = $is_success;
$this->message = $message;
$this->code = $code;
$this->data = $data;
}
public function render()
{
return response()->json([
'success' => $this->is_success,
'message' => $this->message,
'data' => $this->data,
], $this->code);
}
}
But when I am trying to test it using postman, the return I got is not the same as I wrote in NullException. It becomes like this:
In your case you are returning the exception as a response instead of throwing it. That's why it's displayed like this.
You could just have to throw the exception without the try/catch:
$check_api_key = $attendance_libraries->check_api_key($this->request);
if ($check_api_key == null) {
throw new NullException(false, 'API Key Not Found', null, 500);
}
The laravel error handler will catch the exception & render it.
EDIT: Or as #miken32 pointed out you could re throw the exception to handle other exceptions:
try {
//...
} catch (NullException $e) {
throw $e;
} catch (// other exceptions) {
}
Instead of Returning the render method from Exception you are returning the Exception. And also you are passing the arguments in wrong order.
Route::get('exception', function () {
try {
$check_api_key = null;
if ($check_api_key == null) {
throw new NullException(
is_success: false,
message: "API Key Not Found",
code: 500
);
}
} catch (NullException $e) {
return $e->render();
} catch (\Exception $e) {
}
});

php manual exception example

So I was reading the php manual on Extending Exceptions and read the example code. My question about the following code is: why does var_dump($o) evaluate to null? Is it because the constructor of the class TestException throws an exception, hence not allowing the completion of the object? I am almost certain that is the reason.
Nevertheless here is the code for examination:
<?php
/**
* Define a custom exception class
*/
class MyException extends Exception
{
// Redefine the exception so message isn't optional
public function __construct($message, $code = 0, Exception $previous = null) {
// some code
// make sure everything is assigned properly
parent::__construct($message, $code, $previous);
}
// custom string representation of object
public function __toString() {
return __CLASS__ . ": [{$this->code}]: {$this->message}\n";
}
public function customFunction() {
echo "A custom function for this type of exception\n";
}
}
/**
* Create a class to test the exception
*/
class TestException
{
public $var;
const THROW_NONE = 0;
const THROW_CUSTOM = 1;
const THROW_DEFAULT = 2;
function __construct($avalue = self::THROW_NONE) {
switch ($avalue) {
case self::THROW_CUSTOM:
// throw custom exception
throw new MyException('1 is an invalid parameter', 5);
break;
case self::THROW_DEFAULT:
// throw default one.
throw new Exception('2 is not allowed as a parameter', 6);
break;
default:
// No exception, object will be created.
$this->var = $avalue;
break;
}
}
}
// Example 1
try {
$o = new TestException(TestException::THROW_CUSTOM);
} catch (MyException $e) { // Will be caught
echo "Caught my exception\n", $e;
$e->customFunction();
} catch (Exception $e) { // Skipped
echo "Caught Default Exception\n", $e;
}
// Continue execution
var_dump($o); // Null
?>
Have a look at PHP exception from PHP official site.
Example:
<?php
class MyException extends Exception { }
class Test {
public function testing() {
try {
try {
throw new MyException('foo!');
} catch (MyException $e) {
/* rethrow it */
throw $e;
}
} catch (Exception $e) {
var_dump($e->getMessage());
}
}
}
$foo = new Test;
$foo->testing();
?>

unexpected T_TRY, expecting T_FUNCTION error message, not sure why?

I am getting unexpected T_TRY, expecting T_FUNCTION error message and am not sure as too why am getting that, can't we use try and catch block inside class like this:
class Processor
{
protected $dao;
protected $fin;
try
{
public function __construct($file)
{
//Open File for parsing.
$this->fin = fopen($file,'w+') or die('Cannot open file');
// Initialize the Repository DAO.
$this->dao = new Dao('some name');
}
public function initiateInserts()
{
while (($data=fgetcsv($this->fin,5000,";"))!==FALSE)
{
$initiate_inserts = $this->dao->initiateInserts($data);
}
}
public function initiateCUpdates()
{
while (($data=fgetcsv($this->fin,5000,";"))!==FALSE)
{
$initiate_updates = $this->dao->initiateCUpdates($data);
}
}
public function initiateExecuteIUpdates()
{
while (($data=fgetcsv($this->fin,5000,";"))!==FALSE)
{
$initiate_updates = $this->dao->initiateIUpdates($data);
}
}
}
catch (Exception $e)
{
}
}
You can't put all your method definitions into one try-catch block.
Instead of
class Foo {
try {
public function func1() { }
public function func2() { }
}
catch(Exception $e) {
}
}
you have to use
class Foo {
public function func1() {
try {
...
}
catch(Exception $e) {
...
}
}
public function func2() {
try {
...
}
catch(Exception $e) {
...
}
}
}
Don't try-catch inside of each method, you could simply try-catch when you use your class:
try {
$p = new Processor($file);
$p->initiateInserts();
$p->initiateCUpdates();
// and so on...
} catch (Exception $e) {
// handle the error...
}
This way your class will be much cleaner and you can decide what to do with errors. Especially if you use your class in multiple places - you can have customized error handling for each case.
You can't have any "do this stuff"-code in a class outside of a method. There is nothing to "try to do" inside those curly brackets, because the stuff inside is just method definitions.

how do I stop further methods in a PHP class?

I am learning OOP with PHP. I am creating a class to extract XML data from a website. My question is how do I stop the given object from executing more methods if there is an error with the first method. For example, I want to send the URL:
class GEOCACHE {
public $url;
public function __construct($url)
{
$this->url=$url;
if (empty($this->url))
{
echo "Missing URL";
}
}
public function secondJob()
{
whatever
}
}
when I write like this:
$map = new GEOCACHE ("");
$map->secondJob("name");
How do I prevent the secondJob method from being executed in that given object without the script terminating?
Throw an Exception in the constructor, therefore the object will never be created
public function __construct($url)
{
$this->url=$url;
if (empty($this->url))
{
throw new Exception("URL is Empty");
}
}
You can then do something like this:
try
{
$map = new GEOCACHE ("");
$map->secondJob("name");
}
catch ( Exception $e)
{
die($e->getMessage());
}
Consider using exceptions in order to control the flow of the script. Throw an exception in the constructor, and catch it outside.
class GEOCACHE {
public $url;
public function __construct($url)
{
$this->url=$url;
if (empty($this->url))
{
throw new Exception("Missing URL");
}
}
public function secondJob()
{
whatever
}
}
try{
$map = new GEOCACHE ("");
$map->secondJob("name");
}catch($e){
// handle error.
}
Throw an exception from __construct
public function __construct($url)
{
if(null == $url || $url == '')
{
throw new Exception('Your Message');
{
}
then in your code
try
{
$geocache = new Geocache($url);
$geocache->secondJob();
// other stuff
}
catch (exception $e)
{
// logic to perform if the geocode object fails
}

Categories