Testing PHP parse errors with phpunit - php

I need to test how our error logger works in various scenarios. One such scenario are parse errors. Here's an example:
public function testParseErrorLogsAnError()
{
$this->assertCount(0, $this->log_handler->getRecords());
try {
eval('<?php not good');
$this->fail('Code above should throw a parse error');
} catch (\Exception $e) {
$this->assertInstanceOf(\ParseError::class, $e);
}
$this->assertCount(1, $this->log_handler->getRecords());
}
Problem is that phpunit always exists with an exception, and never enters catch block. How to disable or orverride phpunit's exception handler, so we can test our own?

For this answer, I'm assuming you're using PHP 7. In PHP 5, Parse Errors cannot be caught and will always terminate your PHP process.
In PHP 7, you can catch Parse Errors using a try/catch statement (contrary to what the other answer says). However, PHP 7's ParseError class extends the Error class, not Exception (see also the documentation). So catch (\Exception $e) will not work, but any of these should:
catch (\ParseError $e) { ...
catch (\Error $e) { ...
catch (\Throwable $e) { ...
Alternatively, use the #expectedException annotation als already suggested by #DevDonkey:
/**
* #expectedException ParseError
*/
public function testParseErrorLogsAnError()
{
eval('<?php not good');
}

Related

Laravel exception not catching

I'm trying to do a very basic exception try catch, but it doesn't catch.
$id =0;
try {
$question = $this->model->find($id); // will not find anything since $id = 0
$question->delete(); // throw an exception
return true;
} catch (\Exception $e) {
dd ('hello'); // should end up here, but no?!?!?
} catch (FatalThrowableError $f) {
echo ("fatal"); // or here... but no.
}
but the catch doesn't "catch". I get an Fatal error in the browser saying that delete was called on a null object. But that's exactly what I was trying to do: do a delete on a null object (id = 0 is not in the DB), to test the exception.
I have tried
use Symfony\Component\Debug\Exception;
use Symfony\Component\Debug\Exception\FatalThrowableError;
or simply
Exception;
FatalThrowableError;
Also, having the \Exception $e or Exception $e (with or without ) doesn't change anything.
Note that if I add a line like $foo = 4/0 I get into the Exception section (dd (hello)).
in .env APP_DEBUG=true, APP_LOG_LEVEL=debug
I'm on Laravel 5.5 using PHP 7.0.10 on windows 7.
http://php.net/manual/en/language.errors.php7.php
As the Error hierarchy does not inherit from Exception, code that uses
catch (Exception $e) { ... } blocks to handle uncaught exceptions in
PHP 5 will find that these Errors are not caught by these blocks.
Either a catch (Error $e) { ... } block or a set_exception_handler()
handler is required.
You can, additionally, catch (\Throwable $e) {} to account for both Error and Exception types.

log4php + PHP 7 - catching and logging Throwable

I am having trouble logging a "TypeError" with log4php. I suspect this is because I recently upgraded from php 5.5 to 7.1.
Usually, my syntax looks like this:
<?
use Logger;
class MyClass
{
/** #var Logger */
private $logger;
function __construct(array $configParams)
{
Logger::configure('logger.xml');
$this->logger = Logger::getLogger(__CLASS__);
}
public function dostuff()
{
try
{
// ...
}
catch (Exception $ex)
{
$this->logger->error("ERROR CAUGHT", $ex);
}
}
}
?>
The above syntax will print lots of info to the log file, including a stack trace. However after reading the latest php7 docs, I believe I'm meant to handle the \Throwable interface, in order to catch both errors and exceptions (which is great). So I replace the above catch with the following:
catch (\Throwable $ex)
This still prints stack-trace information for my exceptions, but when a "TypeError" is caught, nothing gets printed to the log file.
I assume that this is due to log4php not knowing how to log errors. How can I log errors using log4php in a universal way?
Thanks,
I think you should use $ex->getMessage() for error() method your code should be
catch (Exception $ex)
{
$this->logger->error("ERROR CAUGHT", $ex->getMessage());
}
For logging trace you should use trace
catch (Exception $ex)
{
$this->logger->trace("ERROR CAUGHT", $ex);
}
Try to fix this
$this->logger->error("ERROR CAUGHT", $ex);
with
$this->logger->error("ERROR CAUGHT : " . $ex->getMessage());
log4php's documentation says that error method has just one parameter.
It took me a while to figure out why the original file name and line number was not logged by log4php when an exception is thrown. It turned out that my custom exception_handler class was only logging the exception message (by doing $exception->getMessage()), which does not contain the file name nor the line number. All I to do is concatenate that info: $exception->getFile() and $exception->getLine():
public function exception_handler ($exception) {
$logger = Logger9::create();
$logger->info($exception->getMessage()." ".$exception->getFile()." ".$exception->getLine());
}
Don't forget to register the custom handler:
#set_exception_handler(array($this, 'exception_handler'));

Can't catch exceptions in laravel

I have the following situation:
try {
DB::beginTransaction();
$task = new Task();
$task->setTracker("");
//thrown \Symfony\Component\Debug\Exception\FatalThrowableError
DB::commit();
}catch (\Exception $e){
DB::rollBack();
Log::error($e);
//throw $e;
}
I am not entering to the catch area.
Any idea why?
update
This is the error thrown:
[Symfony\Component\Debug\Exception\FatalThrowableError]
Type error: Argument 1 passed to App\Models\Task::setTracker() must be an instance of Carbon\Carbon, integer given, called in /var/www/app/Services/ShareLogic.php on line 60
and will not be catched
Thanks
Catching Throwable did the trick.
Have no idea why?
Anyone does?
It does not catch the exception because you are trying to catch \Exception which Symfony\Component\Debug\Exception\FatalThrowableError does not extend.
Instead try to catch the actual exception by importing it..
use Symfony\Component\Debug\Exception\FatalThrowableError;
And then you can do..
try {
//
} catch(FatalThrowableError e) {
//
}
Edit
Ok, so in addition to the above solution it seems PHP 7+ handles error a bit differently than PHP 5. So try this..
try {
//
} catch(Error $e) {
// This should work
} catch(Throwable $e) {
// This should work as well
}
Symfony's Debug component is much more sophisticated in order to log and report all kinds of errors but take look at this simple example (php 7.1.x):
<?php
class MyUncatchableError extends Exception {}
function myExceptionHandler($e) {
throw new MyUncatchableError('BANG: '.$e->getMessage());
}
set_exception_handler('myExceptionHandler');
$foo = true;
try {
$foo->modify();
} catch (Exception $e) {
echo 'nope';
} catch (MyUncatchableError $e) {
echo 'nope2';
}
What will be the outcome? Well:
Fatal error: Uncaught MyUncatchableError: BANG: Call to a member function modify() on boolean in /in/WJErU:6
Stack trace:
0 [internal function]: myExceptionHandler(Object(Error))
1 {main}
thrown in /in/WJErU on line 6
and you can't catch that exception because you should catch the original.. throwable here, which is Error for this kind of "error". You can catch it by catching "Error" class. And with PHP7 hierarchy it implements Throwable interface, that's why you can't catch it using Exception (because while Exception implements Throwable, Error is no an Exception - see: http://php.net/manual/en/language.errors.php7.php).
And this is true for PHP7+ because with 5.* there was no Throwable nor Error, and doing $foo->modify(); would just stop the script and return a Fatal Error. You can make your own error handler (set_error_handler) and throw an exception there (and Debug component does that for php 5.*) but this method does not work for Fatal Errors. Instead Debug component hooks into script shutdown and reads last error and throws FatalErrorException.
This description may not be completely accurate as I have't dug deeply into Symfony but you can get the idea here.

PHP Custom Error Handler: How to determine if exception was generated within try{}catch{} block?

I have a custom error handlers:
set_error_handler('API_Error_Handler');
register_shutdown_function('Fatal_Error_Handler'); // This one calls API_Error_Handler eventually
In the following example, both catch{} section AND API_Error_Handler are executed.
try{
// Exception raised here
} catch(Exception $e){
// No error reporting needed, do something else
}
I want ONLY catch{} to execute. How do I do that? Maybe determine within API_Error_Handler whether exception is already caught by try-catch? Or are there other approaches available?
Example code:
set_error_handler(function() {
echo "Error is handled by custom error handler. <br>";
});
try{
new SoapClient('http://bad.address/wsdl');
} catch(Exception $e){
echo "Error is caught. <br>";
}
I think the best way will be to create an Exception class by your self that extends Exception:
class MyCustomException extends \Exception {}
and throw this where you need it. Then change
} catch (Exception $e)
to
} catch (MyCustomException $e)
and you should only get your custom exception catched

How to catch exceptions in your ZF2 controllers?

I've setup the ZendSkeletonApplication with ZF 2.0.3 and I am unable to catch exceptions in my controllers. For instance if I put the below piece of code in module/Application/src/Application/Controller/IndexController.php:
public function indexAction() {
echo "BEFORE\n";
try {
throw new \Exception("My exception");
} catch (Exception $e) {
echo "Caught exception $e\n";
exit;
}
and access the page I get:
BEFORE
An error occurred
An error occurred during execution; please try again later.
Additional information:
Exception
File:
module/Application/src/Application/Controller/IndexController.php:25
Message:
My exception
the ViewModel kicks in and displays the exception, effectively preventing me from catching it.
How can I catch exceptions in ZF2 controllers?
You are throwing PHP's generic Exception
throw new \Exception("My exception");
but you catch the Exception from the current namespace
} catch (Exception $e) {
Assuming your controller is in Application\Controller, you either have to declare
use \Exception;
above your class to import the global Exception into the current namespace or
} catch (\Exception $e) {
to catch PHP's global Exception.

Categories