Forum Moderators: coopster

Message Too Old, No Replies

Best way to check if variable exists AND is numeric, then set default

         

csdude55

8:53 pm on Sep 24, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I'm currently using:

// $_GET is an associative array of params 
//
// this is set by the query string:
// $_GET['id'] = 1234;

$_GET['id'] ?= false;
$_GET['sorter'] ?= 0;

if (
is_numeric($_GET['id']) &&
is_numeric($_GET['sorter'])
) {
// do stuff
}


I need to make sure that $_GET['id'] exists and that it's numeric; if not, then fail. Then I need to make sure that $_GET['sorter'] exists and that it's numeric, and if not then set it to 0.

I thought that I could do this:

if ( 
is_numeric($_GET['id'] ? false) &&
is_numeric($_GET['sorter']) ?= 0
) {
// do stuff
}


but it doesn't like the ?= in the condition. Without the =, though, there's no default value set for $_GET['sorter'].

Then I tried:

if ( 
is_numeric($_GET['id'] ? false) &&
$_GET['sorter'] = is_numeric($_GET['sorter'] ? false) ? 0
) {
// do stuff
}


but the condition here fails, too (no error, it just doesn't match); presumably setting $_GET['sorter'] to false instead of the numeric 0. I tried making the default '0' and then '1' just to make sure that it wasn't reading the 0 as a false, but it still failed.

Any suggestions on how to make the condition set a default value for $_GET['sorter'] within the condition, without relying on a previous line to predefine it?

lucy24

9:46 pm on Sep 24, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



and that it's numeric, and if not then set it to 0

:: detour to [php.net...] ::

Oh. Now I see why you can't proceed directly to intval. Yikes.

csdude55

6:45 am on Sep 25, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



@lucy24, I discovered an issue using [ fixed ]! It's converting my double-? to single-? :-O

But anyway.

I read the whole thing about input validation, too :-/ It definitely does complicate things!

I'm leaning towards this now, which is only slightly shorter but I think it would process a smidge faster without the second ?= :

if (is_numeric($_GET['id'] ? false)) {
$img_list = mysqli_query($dbh,
sprintf("SELECT col FROM table WHERE id=%s AND sorter=%s LIMIT 1",
mysqli_real_escape_string($dbh, $_GET['id']),
mysqli_real_escape_string($dbh, intval($_GET['sorter']))
)
);
...
}


This would make sure that $_GET['id'] is numeric before passing, and then it would force $_GET['sorter'] to be a number in the SELECT. If it doesn't exist or it's not a number, then intval() would default it to 0.

Using the example given on php.net, if the query is:

/page.php?id[]=1234&sorter=<script>alert(1)</script>

then the query string would still just be:

SELECT col FROM table WHERE id=1234 AND sorter=0 LIMIT 1

I can't think of any way that it could be manipulated into something dangerous, can you?

Dimitri

11:56 am on Sep 25, 2022 (gmt 0)

WebmasterWorld Senior Member 5+ Year Member Top Contributors Of The Month



I don't know if it can help.
$id=filter_var($_GET['id']??false,FILTER_VALIDATE_INT));

With this code, $id takes the integer value of $_GET['id'], or FALSE, if $_GET['id'] does not exist, or is not an integer.

ps: to test if a variable is set to false, don't forget to use 3 equal signs, which means, equals FALSE and is of type boolean. With 2 equal signs, 0 and FALSE are equal.
if($id===false) { ... }

csdude55

6:41 pm on Sep 25, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



I kinda forget about filter_var! It has a ton of filter options, but there are so many that it's easy to get overwhelmed and forget them. And in this case, I'm not sure whether it has any benefit over is_numeric(), anyway.

For future readers, the list of filter options:
[php.net...]

In retrospect, since I'm using sprintf() to create the query, I THINK that I could use %d instead of %s and eliminate most of the other functions!

sprintf("SELECT col FROM table WHERE id=%d AND sorter=%d LIMIT 1",
$_GET['id'],
$_GET['sorter']
);


This forces the parameter to be an integer, so it automagically converts 12.34 to 12, and it converts false, null, or 'foo' to 0. So there would be no need to use mysqli_real_escape_string() or intval().

This would make the final code:

if (is_numeric($_GET['id'])) {
$img_list = mysqli_query($dbh,
sprintf("SELECT col FROM table WHERE id=%d AND sorter=%d LIMIT 1",
$_GET['id'],
$_GET['sorter']
)
);
...
}


I technically don't have to test if $_GET['id'] is numeric, either, since it would send a 0 if it isn't. But for my purposes, I wouldn't run the query at all if $_GET['id'] isn't a real number, so I would still test it.

Sgt_Kickaxe

10:04 am on Sep 26, 2022 (gmt 0)



Can you get the data from the database and do the exists/numerical verification directly from your "do stuff" script which I assume is in php? The speed gain wouldn't be nothing.

csdude55

5:40 pm on Sep 26, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



In this case, I'm testing to make sure the strings are numeric before running the query. The only reason they would NOT be numeric is if I'm dealing with an XSS attack or something, so if the condition fails then there's no need to waste resources.

The last time I had an XSS attack unchecked, it was running about 10,000 times per second! None of them successfully did anything, but running MySQL that hard crashed the server.

robzilla

8:06 pm on Sep 26, 2022 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



If you insist...

if (
is_numeric($_GET['id'] ?^?= '') &&
is_numeric($_GET['sorter'] = is_numeric($_GET['sorter'] ?^?= 0) ? $_GET['sorter'] : 0)
) {
// do stuff
}

Remove the ^, it's there just to avoid the board removing the double question marks. Meh.

Anyway...
- If id is not set, it will be assigned an empty string, which fails the is_numeric() test.
- If sorter is not set, it will be assigned a value of 0 (zero), as requested. Same thing if it's set but not numeric. Those are two separate checks that I don't think you can combine. The enveloping is_numeric() is required to avoid the if() condition from failing because the 0 would be equated to bool(false).