Php for loop with try catch - php

This question is in continuation from this as suggested by one of the user.
I am using the getIDs function as below to process the id's. CheckValid() will check if the id's is a valid one to be processed, if yes then it will go to the next one updateUsers(). Check valid just checks for a condition and if not it throws an exception. updateUsers() just updates a column if it passes checkValid().
Problem – If I get 4 id's as output from getIDs(), and with the execute(), it process 2 for example and if it fails for 2nd id, it doesn't continue for the rest 2 id's ..I want it to continue so I commented out the "throw $e in the catch block".
Function execute() {
for($i=0 ; $i<count($this->getIDs()); $i++) {
try {
$this->checkValid();
$this->updateUsers();
} catch(Exception $e) {
//throw $e;
}

have you try a simple continue in the catch block ? didn't test but maybe something like that:
Function execute() {
for($i=0 ; $i<count($this->getIDs()); $i++) {
try {
$this->checkValid();
$this->updateUsers();
} catch(Exception $e) {
//throw $e;
continue; // if not working try a continue 2;
}
}
}

It sounds like you're using exceptions as booleans, I'd suggest avoiding that, as it gets confusing quickly, unless you really need the contents of the exception. See if this makes any sense for your use case (I'll grant, it may not).
// returns true if valid, false otherwise
function checkValid(){
try {
// do your validation
return true;
} catch (Exception $e) {
// optional: save the exception in case we want to know about it
$this->last_error = $e;
return false;
}
}
function execute() {
for($i=0 ; $i<count($this->getIDs()); $i++) {
if($this->checkValid()){
$this->updateUsers();
}
// if you want to do something with an error, simply add an else clause
// and handle $this->last_error
}
}
Also, I obviously don't know your code or what exactly you're doing, but looping n times and calling checkValid() and updateUsers() without parameters seems like very poor practice. Much better to, for instance, loop over the list of IDs and check each ID and user in turn, something like this:
foreach($this->getIDs() as $id){
if($this->checkValid($id)){
$this->updateUser($id);
} else {
// an advantage of this is now we can know exactly which ID failed,
// because we have the $id variable
}
}

Related

Dealing with spatie asynchronous error handling

I'm working on an asynchronous process on a PHP project. I'm using a library named spatie/async. The code snippet is like below :
foreach (range(1, 2) as $i) {
$pool->add(function () use ($i) {
// Do a thing
try {
$result = $i / 0; // This will cause an error
return "Works";
} catch (\Exception $e) {
return -1;
}
})->then(function ($output) {
// Handle success
echo (output . "\n");
})->catch(function ($exception) {
// When an exception is thrown, it's caught and passed here.
echo "Sounds good, but don't work\n";
})
}
$pool->wait();
All I want is when the $result got an error, it will go into the inner catch, but instead, it goes down to the bottom catch which causing a different result from what I want.
The result that I want is :
-1
-1
But instead, the result is :
Sounds good, but don't work
Sounds good, but don't work
Can anyone help me to achieve the result as I want?
The problem of your code is, that it does not throw an Exception in the add method call. A division by 0 is just causing an error, but not ein exception. Instead of changing the whole php error handler, I 'd suggest to extend your logic a little bit in your add method call.
$divisor = 0;
$pool->add(function() use ($i, $divisor) {
try {
if ($divisor === 0) {
throw new \LogicException('Division by zero!');
}
return $i / $divisor;
} catch (\LogicException $e) {
return -1;
}
});
Another solution could be changing the error handling for the pool method call.
set_error_handler(function () {
throw new \LogicException('Ouch!');
});
$pool->add(function() use ($i) {
try {
$result = $i / 0;
} catch (\LogicException $e) {
return -1;
}
});
restore_error_handler();
Caution! Changing the error handler affects all upcoming errors. Even the errors thrown in your used library. Keep in mind, that these are code snippets. This is not tested or thougt to be used in production. Hope that helps out a little bit.

Returning true/false in functions with a message to further explain the reason why it returned false

I'm creating functions where I want to be able to output true or false pertaining to if statements within the function are met. An example of this is like this:
function pokeme($number){
if($number > 10)
{
return true;
}
else
{
return false;
}
}
However, one problem with this approach is that if the function returns false, you wont be able to decipher which false means what, especially if there is more than one if/else statements.
My question is, how do I output a false plus a way to later identify what that false was?
Do I do an array?
return array("false", "message pertaining to whatever");
However, if that is done, you can't really do this, plus...:
if(pokeme()){ /*success*/ } else { /*there may be multiple falses for different situations... how do I distinguish what it is? */}
Note that the way the idea is demonstrated here might not be the best, but once you get te hang of it, it gets easier. Also, read end note, please.
If you want to use like this (true is expected and false is problem):
if(pokeme()){ /*success*/ } else { /* not needed */}
You can do something like this:
function pokeme($number){
//let's say you want to return true if it's >10 and -9 to -1
if($number > 10){
// do something
return true;
}
if($number < 0 && $number > -10){
return true;
}
// handling multiple problems (just 2 ^^)
if($number < -9){
throw new Exception("Invalid input. Can't use negative smaller than -9.");
}
throw new Exception('Invalid input. Expected bigger than 10.');
}
Two tests:
try{
echo "RESULT1 :".pokeme(-42).":"; // not shown (error in this line)
echo "RESULT2 :".pokeme(11).":"; // not shown
}catch(Exception $e){
echo "Error: '".$e->getMessage()."'"; // Just the message
}
echo "<br><br>";
try{
echo "RESULT3 :".pokeme(11).":<br>"; // shown
echo "RESULT4 :".pokeme(10).":"; // not shown (error in this line)
}catch(Exception $e){
echo $e; // Full error
}
You can use it like this:
try{
if(pokeme(11)){
echo "VALID INPUT<br>";
}
if(pokeme(5)){
echo "I'm not seen :\\";
}
}catch(Exception $e){
echo "Error: '".$e->getMessage()."'";
}
End note: Think of this like you are using a built-in php function that might cause an error. This way you have to handle it with a try..catch.
More about Exceptions.
For fully automated input validations you can make use of the Symfony form component, and its Validation.
You can also add very simple constraints like LessThan(10) exactly like in your example, and the component automatically writes the appropriate error message back to your page (and the invalid form e.g. dont performs DB inserting).
It exists a lot of prepared constraints to use, you can also create own ones.
Or if you want to write all by your self, i suggest you to read OOP and Exception handling before.
EDIT
If you want to "collect" errors with its messages, solving this procedural for your code example (not recommended) you can store this messages in a temporary array in the superglobal variable $_SESSION. I explicitly say superglobal variable. Dont use a global variable and inject it with the global key, this will become very complex code in long therm.
Anyway my idea using $_SESSION. This code works for me:
<?php
session_start();
// Resetting the tmp session array on every page reload. That previous error messages are resetted.
$_SESSION['errors'] = array();
function printSessionErrors()
{
// Print errors stored in the session if some exists
if (array_key_exists('errors', $_SESSION)) {
foreach ($_SESSION['errors'] as $i => $error) {
$success = true === $error['success'] ? 'true' : 'false'; // Only for print
$message = $error['message'];
echo "Success: $success. Message: $message <br>";
}
}
}
function pokeme($number)
{
$expected = 10;
$success = null;
if ($number > $expected) {
$success = true;
} else {
$_SESSION['errors'][] = array(
'success' => $success,
'message' => "Number $number is less than $expected"
);
$success = false;
}
return $success;
}
pokeme(1);
pokeme(7);
pokeme(99);
printSessionErrors();
Now depending on if it was a form POST or procedural validation you add printCleanSessionErrors() on top (after session_start()) or on bottom on the code.
I get this output:
Success: false. Message: Number 1 is less than 10
Success: false. Message: Number 7 is less than 10
You only have to add the $_SESSION['errors'][] = array .... into your other error situations.
That's one of those questions, which, being very poorly phrased, lead to completely wrong answers.
Before asking such a question, one has to realize that there are two kinds of functions:
functions that perform some action
functions that do nothing but just verify the parameter, telling whether it's right or wrong
And the approach for these two kinds must be completely different.
In case pokeme() function has to use the $number parameter somehow, but for some reason cannot do that, then an exception must be thrown. Because it's the very purpose for exceptions: to handle the exceptional behavior. In this case, the function will never returns false, so it makes no sense to return true either. So it will be either the correct result or exception will be thrown. But it is important to understand that as a rule, exceptions shouldn't be caught on the spot. Least an exception has to be used to convey some message from a function.
An exception should be used only if a function is unable to do its job:
function divide_by($number, $divisor)
{
if (!is_numeric($number) || !is_numeric($divisor)) {
throw new InvalidArgumentException("The arguments must be nubmers");
}
if ($divisor === 0) {
throw new InvalidArgumentException("Cannot divide by zero");
}
return $number / $divisor;
}
In case pokeme() function, as it shown in the example provided, has to test the $number somehow, and tell if it's all right or not, no exceptions have to be used. Because wrong value is not an exceptional case.
Here, the simplest approach to convey the message would be to reverse the function's "polarity" and make it return false in case the parameter is all right, and a true-ish value in case it's wrong. Which will allow us to return the very message we need.
function check_number($number)
{
if(!is_int($number))
{
return "The value provided is not numeric";
}
if($number < 10)
{
return "The number is less than 10";
}
return false;
}
and then it can be used like this
$error = check_number($number);
if(!$error) {
/*success*/
} else {
$errors[] = $error;
}
But this approach is rather silly. A more robust solution would be to write a class, utilizing such a handy feature as class variables
class NumberValidator
{
protected $error;
public function validate()
{
if(!is_numeric($number))
{
$this->error = "The value provided is not numeric";
return false;
}
if($number < 10)
{
$this->error = "The number is less than 10";
return false;
}
return true;
}
public function getError()
{
return $this->error;
}
}
and then it can be used like this
$validator = new NumberValidator();
if($validator->validate($number)) {
/*success*/
} else {
$errors[] = $validator->getError();
}

Laravel DB::transaction() return value

It's my first time to use DB::transaction() but how exactly does it work if a transaction fails or is successful? In the example below, do I have to manually assign a value to return true, or if it fails will the method either return false or totally exit the transaction (therefore skipping the rest of the code)? The docs aren't so helpful on this.
use Exception;
use DB;
try {
$success = DB::transaction(function() {
// Run some queries
});
print_r($success);
} catch(Exception $e) {
echo 'Uh oh.';
}
Solution
I wrote down this solution for others who might be wondering.
Since I was more concerned about returning a boolean value depending on the success of my query, with a few modifications it now returns true/false depending on its success:
use Exception;
use DB;
try {
$exception = DB::transaction(function() {
// Run queries here
});
return is_null($exception) ? true : $exception;
} catch(Exception $e) {
return false;
}
Take note that the variable $exception is never returned since if something goes wrong with your query, the catch is immediately triggered returning false. Thanks to #ilaijin for showing that an Exception object is thrown if something goes wrong.
By giving a look at function transaction it does its process inside a try/catch block
public function transaction(Closure $callback)
{
$this->beginTransaction();
// We'll simply execute the given callback within a try / catch block
// and if we catch any exception we can rollback the transaction
// so that none of the changes are persisted to the database.
try
{
$result = $callback($this);
$this->commit();
}
// If we catch an exception, we will roll back so nothing gets messed
// up in the database. Then we'll re-throw the exception so it can
// be handled how the developer sees fit for their applications.
catch (\Exception $e)
{
$this->rollBack();
throw $e;
}
So throws an Exception (after the rollback) if fails or returns $result, which is the result of your callback
There is a short version if you want to use the default transaction method that ships with Laravel without handling it manually.
$result = DB::transaction(function () {
// logic here
return $somethingYouWantToCheckLater;
});
You can also use the following
DB::rollback();

Returning from an Exception

This is probably going to sound like a sci-fi request, though – is there a way to return to the beginning of the try {} block where Exception was thrown from within the catch() {} block?
Here is an example:
try
{
// make OAuth request
}
catch(OAuthException $e)
{
// if(){}
// If tells me that the Exception was thrown because the access token is expired
// I have alternative access token (that's always up to date, but there is a catch why I void using it)
// I set it as a new access token and want to repeat the try {} block
}
Obviously goto could do it, though, I am looking if there is a more sophisticated approach.
You can wrap the code up inside a function and call the same function from the catch section
function remotePost($accessToken){
try{
}catch(OAuthException $e){
//the one used is not alternative token and if there is an alternative access token
return remotePost($alternativeAccessToken);
}
}
A while loop.
do {
$ok = false;
try {
// something
$ok = true;
} catch (...) {
// something
}
} while (!$ok);
AksharRoop's and Broncha's solution is also nice, especially if you have a limited number of backup plans (i.e. for the specific scenario you describe). Using while is somewhat more general.
Move your try block to a separate function so that you can call it again with a new token.
try
{
MakeAuthRequest(token);
}
catch(OAuthException $e)
{
if (failedDueToToken)
{
MakeAuthRequest(newToken);
}
}

PHP exceptions in foreach loop

Which is the right way to implement php exception (try{}catch(){}) in a foreach loop that looks like this:
foreach ($apis as $api)
{
$api = '_'.$api;
$searchResults[$api] = $this->$api($parameters);
}
I want to implement the php exceptions for if one of the $this->api(); returns an error message, than catch it and do a if inside the catch to display the right message for the error message returned.
Edit:
Also, when capturing the error and if the error message is 1 (for example) is it a good way to do:
$searchResults['api'] = $this->_api($parameters);
so it tries to do the function again and see if this time it brings valid data?
foreach ($apis as $api)
{
$api = '_'.$api;
try {
$searchResults[$api] = $this->$api($parameters);
}
catch(ParameterException $e) {
// parameterexception handling here
echo "A ParameterException was thrown";
}
catch(Exception $e) {
// All other exceptions
echo "Some other Exception was thrown";
}
}
You can differentiate between more Exception-Types as well.
Since the catch block will be executed only in case of an exception, it really makes no difference if you wrap the for-each loop inside the try block, or if you put the try-catch inside the loop's body.
You should take whatever adds more clarity. However, doing it inside the loop will enable you to handle more specific exceptions relevant to the loop's body if the need arises in the future.
Also, since exceptions are typed, you don't need to do an if, just put different catch clauses:
try {
:
} catch (FirstExceptionType $e) {
:
} catch (SecondExceptionType $e) {
:
}
The code you use will always fail, because you are trying to use a variable ($this->$api($params);) as a function. How ever, you could implement your try-catch as follows:
foreach ($apis as $api)
{
$api = '_'.$api;
try {
$searchResults[$api] = $this->$api($parameters);
}
catch(Exception $e) {
// handle exception
}
}
You can also handle multiple Exceptions of different types by adding another catch() with another Exception class inside it, like:
foreach ($apis as $api)
{
$api = '_'.$api;
try {
$searchResults[$api] = $this->$api($parameters);
}
catch(OtherException $e) {
// Handle it
}
catch(Exception $e) {
// Handle it
}
}

Categories