Problem
Hey, my array $isActiefErr and $naamErr doesn't fill up after I throw an exception, but the foreach loop does continue to validate the values. I know the foreach loop keeps on looping, because the array which should have all the values is filled, but the error array stops after it encounters its first error.
What I have tried:
I've tried to print the array but it only pushes the first error into my array. It then stops validating. This also shows in my error log. I think the problem would be the try/catch, but I simply cannot confirm this, because of my lack of knowledge regarding try/catches.
Question
How can I get the array to push and display all the errors with a try/catch block surrounding it? Or am I simply missing something and isn't the problem related to the try/catch?
This is my code:
$validateOk = 0;
$setVar = new SetVariable();
$teller = 1;
$save = null;
$validateOk = 0;
$con->beginTransaction();
$weg = TypeQuery::create()->find();
$weg->delete();
try {
foreach ($_POST as $key => $value) {
if (isEven($teller)) {
$validateOk += $setVar->nameval($key)
->onerror($isActiefErr[]) //errors aren't being pushed into this array, besides the first one.
->validator(v::numericVal())
->go();
if ($validateOk == 0) {
$save->setCode(substr($key, 1));
$save->setIsActief($value);
$save->save();
} else {
//ROLLBACK/CATCH
throw new Exception($error);
}
} else {
$validateOk += $setVar->nameval($key)
->onerror($naamErr[])
->validator(v::alpha('/()éá&., '))
->go();
if ($validateOk == 0) {
$save = new Type();
$save->setCode(substr($key, 1));
$save->setNaam($value);
} else {
//ROLLBACK/CATCH
throw new Exception($error);
}
}
$teller += 1;
}
$con->commit();
} catch (Exception $e) {
$error = "Het opslaan is fout gegaan!";
} {
$con->rollback();
}
About the following code, how can I go to finally without throw an Exception in PHP?
try {
$db = DataSource::getConnection();
if (some condition here is TRUE) {
// go to finally without throw an exception
}
$stmt = $db->prepare($sql);
$stmt->saveMyData();
} catch (Exception $e) {
die($e->getMessage());
} finally {
$db = null;
}
Please don't do this, but here's an option:
try {
if (TRUE){
goto ugh;
}
echo "\ndid not break";
ugh:
} catch (Exception $e){
echo "\ndid catch";
} finally {
echo "\ni'm so tired";
}
I strongly encourage against using a goto. I think it's just really easy for code to get sloppy & confusing if you're using goto.
I'd recommend:
try {
if (TRUE){
echo "\nThat's better";
} else {
echo "\ndid not break";
}
} catch (Exception $e){
echo "\ndid catch";
} finally {
echo "\ni'm so tired";
}
You just wrap the rest of the try into an else in order to skip it.
Another option could be to declare a finally function, call that, and return.
//I'm declaring as a variable, as to not clutter the declared methods
//If you had one method across scripts, naming it `function doFinally(){}` could work well
$doFinally = function(){};
try {
if (TRUE){
$doFinally();
return;
}
echo "\ndid not break";
} catch (Exception $e){
echo "\ndid catch";
} finally {
$doFinally();
}
If you needed to continue the script, you could declare $doFinally something like:
$doFinally = function($reset=FALSE){
static $count;
if ($reset===TRUE){
$count = 0;
return;
} else if ($count===NULL)$count = 0;
else if ($count>0)return;
}
Then after the finally block, you could call $doFinally(TRUE) to reset it for the next try/catch
I have a PHP platform where user write mongodb query like picture below
and following code print result as a table
$m = new MongoClient();
$db = $m->Forensic;
$coll= $db->mobile_data;
if (isset($_POST['txt_area']) && !empty($_POST['txt_area'])) {
$d = ($_POST['txt_area']);
$p = json_decode($d);
$user_code = $coll->find($p);
When I type correct code system able to ouput all my result but if I write query wrong I am getting error message like
Warning: MongoCollection::find(): expects parameter 1 to be an array or object, null given in C:\xampp\htdocs\reports5.php on line 126
to catch that error i have tried following try catch code but no luck
try {
if (isset($_POST['txt_area']) && !empty($_POST['txt_area'])) {
$d = ($_POST['txt_area']);
$p = json_decode($d);
$user_code = $coll->find($p);
$NumberOfRow2 = $user_code->count();
$user_code->sort(array('Chat_group' => -1, 'Instant_Message' => 1 ));
}
}
catch (MongoCursorException $e) {
echo "error message: ".$e->getMessage()."\n";
echo "error code: ".$e->getCode()."\n";
}
catch (MongoException $e)
{
echo $e->getMessage();
}
catch(MongoResultException $e) {
echo $e->getMessage(), "\n";
$res = $e->getDocument();
var_dump($res);
}
what would be the best way to catch above error
The warning you're seeing is PHP complaining that $coll->find($p); expects $p to be either array or object while it's null. Quoting json_decode documentation:
NULL is returned if the json cannot be decoded or if the encoded data is deeper than the recursion limit.
So a proper way to defend against warning would be:
$p = json_decode($d);
if ($p === null) {
echo "Provided query is invalid!";
} else {
// do your logic
}
I have simple queue worker based on standard AMQP Class from PHP. It works with RabbitMQ as a server. I have Queue Class for initialize AMQP connection wirh RabbitMQ. Everything works fine with code below:
$queue = new Queue('myQueue');
while($envelope = $queue->getEnvelope()) {
$command = unserialize($envelope->getBody());
if ($command instanceof QueueCommand) {
try {
if ($command->execute()) {
$queue->ack($envelope->getDeliveryTag());
}
} catch (Exception $exc) {
// an error occurred so do some processing to deal with it
}
}
}
However I wanted to fork queue command execution, but in this case queue goes endless with the first command over and over again. I can't acknowledge RabbitMQ that message was recieved with $queue->ack(); My forked version (simplified with only one child for testing sake) looks like this :
$queue = new Queue('myQueue');
while($envelope = $queue->getEnvelope()) {
$command = unserialize($envelope->getBody());
if ($command instanceof QueueCommand) {
$pid = pcntl_fork();
if ($pid) {
//parent proces
//wait for child
pcntl_waitpid($pid, $status, WUNTRACED);
if($status > 0) {
// an error occurred so do some processing to deal with it
} else {
//remove Command from queue
$queue->ack($envelope->getDeliveryTag());
}
} else {
//child process
try {
if ($command->execute()) {
exit(0);
}
} catch (Exception $exc) {
exit(1);
}
}
}
}
any help will be appreciated...
I finally solved the problem! I had to run ack command from child process, it works this way!
This is correct code:
$queue = new Queue('myQueue');
while($envelope = $queue->getEnvelope()) {
$command = unserialize($envelope->getBody());
if ($command instanceof QueueCommand) {
$pid = pcntl_fork();
if ($pid) {
//parent proces
//wit for child
pcntl_waitpid($pid, $status, WUNTRACED);
if($status > 0) {
// an error occurred so do some processing to deal with it
} else {
// sucess
}
} else {
//child process
try {
if ($command->execute()) {
$queue->ack($envelope->getDeliveryTag());
exit(0);
}
} catch (Exception $exc) {
exit(1);
}
}
}
}
I am executing this statement under while (($data=fgetcsv($this->fin,5000,";"))!==FALSE)
Now what I want in else loop is to throw exception only for data value which did not satisfy the if condition. Right now am displaying the complete row as I am not sure how to throw exception only for data which does not satisfy the value.
Code
if ((strtotime($data[11]) &&strtotime($data[12])&&strtotime($data[16]))!==FALSE
&& ctype_digit($data[0]) && ctype_alnum($data[1]) && ctype_digit($data[2])
&& ctype_alnum($data[3]) && ctype_alnum($data[4]) && ctype_alnum($data[5])
&& ctype_alnum($data[6]) && ctype_alnum($data[7]) && ctype_alnum($data[8])
&& $this->_is_valid($data[9]) && ctype_digit($data[10]) && ctype_digit($data[13])
&& $this->_is_valid($data[14]))
{
//Some Logic
}
else
{
throw new Exception ("Data {$data[0], $data[1], $data[2], $data[3],
$data[4], $data[5], $data[6], $data[7],
$data[8], $data[9], $data[10], $data[11], $data[12],
$data[13], $data[14], $data[16]} is not in valid format");
}
Guidance would be highly appreciated as to how can I throw exception only for data which did not satisfy the if value.
Why not separate the tests ? i.e. do each test one by one, and throw an exception if a specific test fails ?
Copy-pasting from your code, it would probably look like this :
if (!strtotime($data[11])) {
throw new Exception("field 11 : {$data[11]} is not a valid date");
}
if (!strtotime($data[12])) {
throw new Exception("field 12 : {$data[12]} is not a valid date");
}
// Some more...
if (!ctype_alnum($data[8])) {
throw new Exception("field 8 : {$data[8]} is not a valid alnum");
}
// And so on...
// And when all is tested, you know the items
// in $data are all OK
This way :
You can know which field caused a validation failure
If you have several distinct tests on the same field, you can know which specific test failed
And, as a possibility, you could (if needed) thrown different kind of exceptions, depending on the test that failed (i.e. one kind of exception for dates, one for integers, ...) -- in some cases, that might be useful.
Edit after the comment : more full example
Yes, you can validate field by field, and still work line by line.
You just have to wrap your testing code in a try/catch block, that's inside the loop that goes line by line ; a bit like that :
$validData = array();
$errors = array();
while ($data = fgetcsv($f)) {
try {
// Validate the data of the current line
// And, if valid, insert it into the database
if (!strtotime($data[11])) {
throw new Exception("field 11 : {$data[11]} is not a valid date");
}
if (!strtotime($data[12])) {
throw new Exception("field 12 : {$data[12]} is not a valid date");
}
// Some more...
if (!ctype_alnum($data[8])) {
throw new Exception("field 8 : {$data[8]} is not a valid alnum");
}
// And so on...
// And when all is tested, you know the items
// in $data are all OK
// => which means it can be inserted into the DB
// Or you can just put the valid data into a "temporary" array, that will be used later :
$validData[] = $data;
} catch (Exception $e) {
// An error has occurend on the current line
// You can log it, if necessary :
$errors[] = $e->getMessage();
}
}
// Now that the whole file has been read, test if there's been an error :
if (empty($errors)) {
// No error
// => insert the data that's in $validData
} else {
// There's been an error
// => don't insert the data that's in $validData, if you don't want to insert anything
}
With that :
If there is an exception on one line (i.e. validation fails), you'll jump to the catch block, to deal with the problem
And the loop will then restart, for the next line.
EDIT: (By Yacoby)
Rather than having endless if statments, you could just define which elements should be checked by which function and then process them in a loop. That way it avoids having 16 if statements.
Code example:
foreach ( array(11,12,16) as $index ){
if ( !strtotime($data[$i]) ){
throw new Exception("field {$i} : {$data[$i]} is not a date");
}
}
foreach ( array(1,3,4,5,6,7,8) as $index ){
if ( !ctype_alnum($data[$i]) ){
throw new Exception("field {$i} : {$data[$i]} is not alphanumeric");
}
}
foreach ( array(2, 10) as $i ){
if ( !ctype_digit($data[$i]) ){
throw new Exception("field {$i} : {$data[$i]} is not a valid digit");
}
}
if ( !$this->_is_valid($data[14]) ){
throw new Exception("field {14} : {$data[14]} is not valid");
}
You'll need to break up your massive if statement into one per value.
I think you'd be better off breaking this up into many if statements like
if(!strtotime($data[11])
{
throw new Exception("...");
}
if(!strtotime($data[12]))
{
throw new Exception("...");
}
//after all of your if statements now do business logic
//remember that all of your if conditions have to be met to get this far
//because throwing the exception will leave this stack
//so it functions sort of like the else clause would.