Sure, just catch the exception where you want to continue execution...
try
{
SomeOperation();
}
catch (SomeException $ignored)
{
// do nothing... php will ignore and continue
// but maybe use "ignored" as name to silence IDE warnings.
}
Of course this has the problem of silently dropping what could be a very important error. SomeOperation() may fail causing other subtle, difficult to figure out problems, but you would never know if you silently drop the exception.
however note that php also has error codes separate from exceptions, a legacy holdover from before php had oop primitives. Most library builtins still raise error codes, not exceptions. To ignore an error code call the function prefixed with @:
c() will always be executed. But if a() throws an exception, b() is not executed.
Only put the stuff in to the try block that is depended on each other. E.g. b depends on some result of a it makes no sense to put b after the try-catch block.
Another angle on this is returning an Exception, NOT throwing one, from the processing code.
I needed to do this with a templating framework I'm writing. If the user attempts to access a property that doesn't exist on the data, I return the error from deep within the processing function, rather than throwing it.
Then, in the calling code, I can decide whether to throw this returned error, causing the try() to catch(), or just continue:
// process the template
try
{
// this function will pass back a value, or a TemplateExecption if invalid
$result = $this->process($value);
// if the result is an error, choose what to do with it
if($result instanceof TemplateExecption)
{
if(DEBUGGING == TRUE)
{
throw($result); // throw the original error
}
else
{
$result = NULL; // ignore the error
}
}
}
// catch TemplateExceptions
catch(TemplateException $e)
{
// handle template exceptions
}
// catch normal PHP Exceptions
catch(Exception $e)
{
// handle normal exceptions
}
// if we get here, $result was valid, or ignored
return $result;
The result of this is I still get the context of the original error, even though it was thrown at the top.
Another option might be to return a custom NullObject or a UnknownProperty object and compare against that before deciding to trip the catch(), but as you can re-throw errors anyway, and if you're fully in control of the overall structure, I think this is a neat way round the issue of not being able to continue try/catches.
An old question, but one I had in the past when coming away from VBA scipts to php, where you could us "GoTo" to re-enter a loop "On Error" with a "Resume" and away it went still processing the function.
In php, after a bit of trial and error, I now use nested try{} catch{} for critical versus non critical processes, or even for interdependent class calls so I can trace my way back to the start of the error.
e.g. if function b is dependant on function a, but function c is a nice to have but should not stop the process, and I still want to know the outcomes of all 3 regardless, here's what I do:
//set up array to capture output of all 3 functions
$resultArr = array(array(), array(), array());
// Loop through the primary array and run the functions
foreach($x as $key => $val)
{
try
{
$resultArr[$key][0][] = a($key);
$resultArr[$key][1][] = b($val);
try
{ // If successful, output of c() is captured
$resultArr[$key][2][] = c($key, $val);
}
catch(Exception $ex)
{ // If an error, capture why c() failed
$resultArr[$key][2][] = $ex->getMessage();
}
}
catch(Exception $ex)
{ // If critical functions a() or b() fail, we catch the reason why
$criticalError = $ex->getMessage();
}
}
Now I can loop through my result array for each key and assess the outcomes.
If there is a critical failure for a() or b().
I still have a point of reference on how far it got before a critical failure occurred within the $resultArr and if the exception handler is set correctly, I know if it was a() or b() that failed.
If c() fails, loop keeps going. If c() failed at various points, with a bit of extra post loop logic I can even find out if c() worked or had an error on each iteration by interrogating $resultArr[$key][2].
try {
// Code that may throw an Exception or Error.
} catch (Throwable $t) {
// Handle exception
}
echo "Script is still running..."; // this script will be executed.
As of PHP 8.0.0, the variable name for a caught exception is optional. If not specified, the catch block will still execute but will not have access to the thrown object.
And thus we can do it like this:
try {
throw new Exception("An error");
}
catch (Exception) {}
You can, but I will warn: many consider this method quite evil.
// https://stackoverflow.com/a/66377817/578023
function is_same(&$a, &$b): bool {
$_ = [ &$a, &$b ];
return
\ReflectionReference::fromArrayElement($_, 0)->getId() ===
\ReflectionReference::fromArrayElement($_, 1)->getId();
}
function attempt_risky_action($collection){
$cursor=NULL;
$resuming = false;
resume:
try{
foreach($collection as $item){
if($resuming && !is_same($cursor,$item) ){
continue; // some things have better ways to skip ahead, especially an array index
}
else {
$resuming = false;
$cursor=&$item; // main concept is to remember where you are in the iteration
} // in some situation you may have to use references, &item
// your normal loop here
.
.
.
}
} catch( Exception $e){
$resuming = repair_something($e, $collection); // returns false if your repair ran out of ideas
if($resuming)
goto resume;
}
unset($cursor);
}
Ideally it would be best to wrap the unset($cursor); call in a finally{} block, but frankly I'm not sure how that plays with goto off hand.
If it executes because goto broke the flow then you will need some conditional logic, so the cursor still exists. If you have a return statement inside the loop you must use a finally block for call to unset($cursor) -- or cause a memory leak.
Then again, while less exciting, you can do this same trick by just nesting your whole loop in do{ try/catch } while($resuming). While that is not LITERALLY reversing your execution, it does exactly the same effect without risking a goto.
function attempt_risky_action($collection){
$cursor=NULL;
$resuming = false;
do{
try{
foreach($collection as $item){
if($resuming && !is_same($cursor,$item) ){
continue;
}
else {
$resuming = false;
$cursor=&$item;
}
// your loop here
}
} catch( Exception $e){
$resuming = repair_something($e, $collection); // returns false if your repair ran out of ideas
}
finally{
if(!$resuming){
unset($cursor);
}
}
} while($resuming);
}
A last method, not pictured; you can use PHP's
reset(), prev(), current(), next(), end() faculties
These will allow you instead to simply have your try/catch block inside a code block that iterates as a loop would -- then use prev() in the catch to try again.