Continue bubbling up inside a loop in PHP - php

I adapted a web script I wrote to fit my needs for some data I need to retrieve from a server. I run this script from a terminal, so error messages are useful information.
The main part of the code is a loop inside a loop, and in that loop I call a function. In this function there's a call to a database. If there is a problem with connecting to the database, I can catch that error with a simple try {} catch(){}, but how should I structure my code so that I can just skip this iteration and move to the next item in the loop? In other words, do a continue only from within a function.
Below is how I would do it, but I am not sure this is the correct way.
foreach ($flavours as $icecream) {
foreach ($sauces as $sauce) {
$amount = dessertServings($icecream, $sauce);
if ($amount != null) {
// Some other functions like orderDessert, makePricingList and so on
fwrite(STDOUT, "$amount servings of $icecream with $sauce remaining!\n");
}
}
}
dessertServings($icecream, $sauce) {
try {
$dbConnection = new Connection("user", "password", "db$icecream$sauce");
$amountOfServings = $dbConnection->query($icecream, $sauce);
return $amountOfServings;
}
// E.g database connection could not be made
catch(Exception $e) {
fwrite(STDERR, $e->getMessage() . "\n");
return;
}
}
Is there a better way to do this?
To make things harder, what if the function doesn't actually return anything and thus isn't assigning a value to a variable? How should you deal with that?
foreach ($flavours as $icecream) {
foreach ($sauces as $sauce) {
prepareDessert($icecream, $sauce);
// Other functions, most importantly: eatDessert($dessert)
}
}
prepareDessert($icecream, $sauce) {
try {
$dbConnection = new Connection("user", "password", "db$icecream$sauce");
$dbConnection->query($icecream, $sauce)->do("prepare");
}
// E.g database connection could not be made
catch(Exception $e) {
fwrite(STDERR, $e->getMessage() . "\n");
}
}
In such a case, how do I make sure that when the try fails, the block in the loop never reaches the other functions, we can't eat an ice cream that wasn't prepared in the first place!
Would I use an empty variable that simply returns true on success and false and fail, and execute the following code in the main block only on true? Or is there a better convention for this in PHP?

how should I structure my code so that I can just skip this iteration and move to the next item in the loop
The first rule of exception handling: do not do exception handling. Allow it to bubble up and catch it when you need it (in your case, in your loop).
You can also re-throw an exception in your catch, if you want to do some processing within the function (like print to STDERR), but let it bubble up!
The other more traditional method is to have your function return some kind of error code - the most basic being "true" on success, "false" or "null" on failure like in your first example. I don't see anything wrong with that.
To make things harder, what if the function doesn't actually return anything and thus isn't assigning a value to a variable? How should you deal with that?
Throw exceptions, that's their job!
how do I make sure that when the try fails, the block in the loop never reaches the other functions
Don't try/catch within the function or re-throw an exception from within your "catch".
Would I use an empty variable that simply returns true on success and false and fail, and execute the following code in the main block only on true? Or is there a better convention for this in PHP?
Yes there is a better convention : throw exceptions.

Related

Function that checks if function executes successfully, otherwise exception. Possible in PHP?

I need some function that will accept a function as the parameter and will run it in try {} catch (Exception $e) {}. If it runs successfully, do nothing, otherwise, throw new Exception. That's something like function-checker, which checks functions if they ran successfully. Anybody can help me or give advice?
Thanks for any replies.
The function should work like that:
function itwillfail () {
echo 10 / 0;
}
check("itwillfail");
Output: Caught exception: Custom exception text here, because it has been thrown as custom.
("check" is that function I need)
What I tried:
function check($func) {
try {
call_user_func($func);
} catch (Exception $e) {
throw new Exception("Custom text here.");
}
}
EDIT: More explained: I need to create function, which do the same as "try" and a lot of different "catch"es for different types of exceptions.
Summarizing your question:
You want a way to call a custom function from a string variable (which you have already figured out that would be via call_user_func($var);.
You then want that function to throw a custom exception error
Confused
What is not clear is the reason you would opt to not define your error handler using the set_error_handler function which would effectively do what your asking and set a switch statement to output different messages based on the error generated.
Example
The following example is not using a call_user_func but it effectively allows you to write how the error will be handled
<?php
function myerror($error_no, $error_msg) {
echo "Error: [$error_no] $error_msg ";
echo "\n Now Script will end";
die();
}
// Setting set_error_handler
set_error_handler("myerror");
$a = 10;
$b = 0;
// Force the error
echo($a / $b);
?>
Not every function throws an exception when they fail. Many functions, especially ones that have been around for a long time, simply trigger PHP errors rather than exceptions.
To handle those, you would use a custom error handler:
https://www.php.net/manual/en/function.set-error-handler.php
So you could set up a custom error handler that would intercept those kinds of failures and throw them as exceptions. The whole point of that function is to do what you're trying to do - handle errors in a custom way.

How can I catch an exception and continue despite an error

I'd like to able to catch an exception and continue with the execution of other subsequent functions (and possibly log an error in the catch section). In the code sample below, there are instances where $html->find doesn't find the element and returns error exception undefined offset. In such cases, the entire script fails. I don't want to specifically test for this error but rather any error that may occur within the code block in the try section.
public function parsePage1($provider)
{
$path = $this->getFile($provider);
$link = $this->links[$provider];
if (file_exists($path)) {
$string = file_get_contents($path);
$html = \HTMLDomParser::str_get_html($string);
$wrapper = $html->find('.classToLookFor')[0];
unset($string);
}
}
try {
$this->parsePage1('nameOfProvider');
} catch(Exception $e) {
// continue...
}
try {
$this->parsePage2('nameOfProvider');
} catch(Exception $e) {
// continue...
}
No, there is no way to make the code within the try block continue past an exception. An exception terminates the function just like a return would; there is no way to restore the state of the function afterwards.
Instead, avoid triggering the error in the first place:
$wrappers = $html->find('.classToLookFor'); # <-- no [0]!
if (count($wrappers)) {
$wrapper = $wrappers[0];
...
}
Just to be clear, the 'error' in this case was a notice. If your errorlevel does not include notices, which is typically the case in production, your code will continue past that point.
With that said, Notices and warnings are intended for developers to add checks for expected input, as in duskwuff's example.
Unfortunatley, duskwuff's answer is problematic with the most recent versions of php at 7.2+. This is because count() expects either an array or an object that implements countable.
With the newest version you will get a Warning:
Warning: count(): Parameter must be an array or an object that implements Countable in
You will be back where you were before using count() only. A simple fix for that is to add a check for is_array.
$wrappers = $html->find('.classToLookFor'); # <-- no [0]!
if (is_array($wrappers) && count($wrappers)) {
$wrapper = $wrappers[0];
...
}
I also want to point out, that per my original comment, the whole purpose of exception catching is to protect against program termination errors.
This was not a good example of the types of errors where you should apply try-catch, but to be clear, your original code does continue... just not within the try section of the code, but after the catch()
This simulation of your original problem illustrates that:
<?php
function findit($foo) {
return $foo[0];
}
try {
findit('');
} catch(Exception $e) {
var_dump($e);
}
echo 'Hey look we continued';
Output will be something like:
Notice: Uninitialized string offset: 0 in ... on line 4
Hey look we continued
I feel this needs to be added as a response because people in the future are going to probably find this question, which really has nothing much to do with try-catch handling, and really has to do with code that expects to work with an array, but might not get one.

Using a return keyword with no return value?

I have been noticing in some PHP design patterns, some authors who write code examples, have return inside the method but it doesn't specify return value .
It just says "return"
Can some one please explain me what is the purpose of doing that? Below is an example
Thank you!
function addListItem(ListItem $listItem){
if(in_array($listItem, $this->listitems, true)){
return;
}
$this->listitems[] = $listItem;
}
That's done for side-effects (IO, altering globals, or the arguments passed by reference, or an object property, like in your example -- $this->listitems[] = $listItem;), or to indicate it's impossible to yield a valid result.
return;
is equivalent to
return null;
The return statement will stop the function immediately after it has been called. Because we do not want any value to be returned like integers, string or booleans, we just stop it so the code will not continue.
This can also be compared to break in a for or while loop.
Well for starters, you may hit an if statement that makes the rest of the code in the method unnecessary.
For example:
if(user->log_in == "")
{
show_error_msg();
return;
}
The "technically more correct" way of writing such code is:
if( !in_array($listItem, $this->listitems, true)) {
$this->listitems[] = $listItem;
}
However, sometimes you may have more complex testing, and proceeding in this manner may result in a single if block spanning several lines with many &&s, or nested if statements that fall off the edge of your screen.
Therefore, it is common to see the pattern of your code, namely "check failure conditions and exit the current block if necessary". This means you can have as few or as many conditions as you want, each with their own failure conditions, and if the code manages to reach the end of it then you're good to go.
In some cases, error handling is useful. Something like:
try {
if( failure condition 1) throw new Exception("Error message 1");
if( failure condition 2) throw new Exception("Error message 2");
// ...
do something here;
}
catch(Exception $e) {
// report error here
}

Is it correct to set up a variable inside a php try-catch?

I know I can. I know it works. But, is it correct? I'm thinking that is like putting a div inside a span: you can but someone will hunt you, find you and destroy you.
// $value comes from $_POST no validation
try {
$value = new DateTime($value);
} catch(Exception $e) {
$value = new DateTime();
}
That's perfectly fine if doing so may result in an exception being thrown. Otherwise you are adding unnecessary overhead to whatever action is being performed.
In your case an exception will be thrown if $value is not a valid value. So your try/catch is necessary to catch the exception and react to it accordingly, which you do.

Using headers in a class

I access a function in an included class Environment which can throw an Alert which needs to direct the users back to a page with some error information. What is the best way for me to reference the page so it can be used wherever I access the function from anywhere?
Is there any better way to do what I am trying to do?
public function checkEnvironment() {
try {
// If status is false
if (!$this->getStatus()) {
// Generate a new special exception with code
throw new Alert(6);
} else {
$connection = Gateway::checkInstance();
return $connection->getData('SELECT * FROM control_environment WHERE subdomain = ?', array($this->subdomain[0]));
}
} catch (Alert $alert) {
$_SESSION['error'] = $alert->getData();
if (!headers_sent()) {
header('Location: my/file/here.php');
exit;
}
}
}
You're badly abusing exceptions. There is absolutely no reason to put a single if statement in a try block, which can only throw one type of exception which is guaranteed to be caught immediately after that block. You've added nothing to your code but clutter, there is literally no advantage to doing this over simply doing an if/else with no exceptions/catching.
The point of exceptions is that you throw them up out of the current scope, to some place where they can actually be handled in a meaningful way.
Pick a real exception class, one that communicates something about the error that's occurred. Alert(6) doesn't tell anybody anything. Then, handle that (and probably many other) exceptions up above this, where you can be more sure that redirecting is the correct course of action. Your low-level database code shouldn't have any concept of browsers or http or redirection.
Your code also shouldn't have an else branch when the purpose of the if branch is to throw an exception. The else is redundant.
The whole function should look like this.
public function checkEnvironment() {
if (!$this->getStatus())
// Generate a new special exception with code
throw new StatusException;
$connection = Gateway::checkInstance();
return $connection->getData('SELECT * FROM control_environment WHERE subdomain = ?', array($this->subdomain[0]));
}

Categories