PHP is_numeric or preg_match 0-9 validation - php

This isn't a big issue for me (as far as I'm aware), it's more of something that's interested me. But what is the main difference, if any, of using is_numeric over preg_match (or vice versa) to validate user input values.
Example One:
<?php
$id = $_GET['id'];
if (!preg_match('/^[0-9]*$/', $id)) {
// Error
} else {
// Continue
}
?>
Example Two:
<?php
$id = $_GET['id'];
if (!is_numeric($id)) {
// Error
} else {
// Continue
}
?>
I assume both do exactly the same but is there any specific differences which could cause problems later somehow? Is there a "best way" or something I'm not seeing which makes them different.

is_numeric() tests whether a value is a number. It doesn't necessarily have to be an integer though - it could a decimal number or a number in scientific notation.
The preg_match() example you've given only checks that a value contains the digits zero to nine; any number of them, and in any sequence.
Note that the regular expression you've given also isn't a perfect integer checker, the way you've written it. It doesn't allow for negatives; it does allow for a zero-length string (ie with no digits at all, which presumably shouldn't be valid?), and it allows the number to have any number of leading zeros, which again may not be the intended.
[EDIT]
As per your comment, a better regular expression might look like this:
/^[1-9][0-9]*$/
This forces the first digit to only be between 1 and 9, so you can't have leading zeros. It also forces it to be at least one digit long, so solves the zero-length string issue.
You're not worried about negatives, so that's not an issue.
You might want to restrict the number of digits, because as things stand, it will allow strings that are too big to be stored as integers. To restrict this, you would change the star into a length restriction like so:
/^[1-9][0-9]{0,15}$/
This would allow the string to be between 1 and 16 digits long (ie the first digit plus 0-15 further digits). Feel free to adjust the numbers in the curly braces to suit your own needs. If you want a fixed length string, then you only need to specify one number in the braces.

According to http://www.php.net/manual/en/function.is-numeric.php, is_numeric alows something like "+0123.45e6" or "0xFF". I think this not what you expect.
preg_match can be slow, and you can have something like 0000 or 0051.
I prefer using ctype_digit (works only with strings, it's ok with $_GET).
<?php
$id = $_GET['id'];
if (ctype_digit($id)) {
echo 'ok';
} else {
echo 'nok';
}
?>

is_numeric() allows any form of number. so 1, 3.14159265, 2.71828e10 are all "numeric", while your regex boils down to the equivalent of is_int()

is_numeric would accept "-0.5e+12" as a valid ID.

Not exactly the same.
From the PHP docs of is_numeric:
'42' is numeric
'1337' is numeric
'1e4' is numeric
'not numeric' is NOT numeric
'Array' is NOT numeric
'9.1' is numeric
With your regex you only check for 'basic' numeric values.
Also is_numeric() should be faster.

is_numeric checks whether it is any sort of number, while your regex checks whether it is an integer, possibly with leading 0s. For an id, stored as an integer, it is quite likely that we will want to not have leading 0s. Following Spudley's answer, we can do:
/^[1-9][0-9]*$/
However, as Spudley notes, the resulting string may be too large to be stored as a 32-bit or 64-bit integer value. The maximum value of an signed 32-bit integer is 2,147,483,647 (10 digits), and the maximum value of an signed 64-bit integer is 9,223,372,036,854,775,807 (19 digits). However, many 10 and 19 digit integers are larger than the maximum 32-bit and 64-bit integers respectively. A simple regex-only solution would be:
/^[1-9][0-9]{0-8}$/
or
/^[1-9][0-9]{0-17}$/
respectively, but these "solutions" unhappily restrict each to 9 and 19 digit integers; hardly a satisfying result. A better solution might be something like:
$expr = '/^[1-9][0-9]*$/';
if (preg_match($expr, $id) && filter_var($id, FILTER_VALIDATE_INT)) {
echo 'ok';
} else {
echo 'nok';
}

is_numeric checks more:
Finds whether the given variable is numeric. Numeric strings consist
of optional sign, any number of digits, optional decimal part and
optional exponential part. Thus +0123.45e6 is a valid numeric value.
Hexadecimal notation (0xFF) is allowed too but only without sign,
decimal and exponential part.

You can use this code for number validation:
if (!preg_match("/^[0-9]+$/i", $phone)) {
$errorMSG = 'Invalid Number!';
$error = 1;
}

If you're only checking if it's a number, is_numeric() is much much better here. It's more readable and a bit quicker than regex.
The issue with your regex here is that it won't allow decimal values, so essentially you've just written is_int() in regex. Regular expressions should only be used when there is a non-standard data format in your input; PHP has plenty of built in validation functions, even an email validator without regex.

PHP's is_numeric function allows for floats as well as integers. At the same time, the is_int function is too strict if you want to validate form data (strings only). Therefore, you had usually best use regular expressions for this.
Strictly speaking, integers are whole numbers positive and negative, and also including zero. Here is a regular expression for this:
/^0$|^[-]?[1-9][0-9]*$/
OR, if you want to allow leading zeros:
/^[-]?[0]|[1-9][0-9]$/
Note that this will allow for values such as -0000, which does not cause problems in PHP, however. (MySQL will also cast such values as 0.)
You may also want to confine the length of your integer for considerations of 32/64-bit PHP platform features and/or database compatibility. For instance, to limit the length of your integer to 9 digits (excluding the optional - sign), you could use:
/^0$|^[-]?[1-9][0-9]{0,8}$/

Meanwhile, all the values above will only restrict the values to integer,
so i use
/^[1-9][0-9\.]{0,15}$/
to allow float values too.

You can use filter_var() to check for integers in strings
<?php
$intnum = "1000022";
if (filter_var($intnum, FILTER_VALIDATE_INT) !== false){
echo $intnum.' is an int now';
}else{
echo "$intnum is not an int.";
}
// will output 1000022 is an int now

Related

What set of chars is php's uniqid composed of?

I would like to prepare simple regular expression for php's uniqid. I checked uniqid manual looking for set of chars used as return value. But the documentation only mention that:
#return string the unique identifier, as a string.
And
With an empty prefix, the returned string will be 13 characters long. If more_entropy is true, it will be 23 characters.
I would like to know what characters can I expect in the return value. Is it a hex string? How to know for sure? Where to find something more about the uniqid function?
The documentation doesn't specify the string contents; only its length. Generally, you shouldn't depend on it. If you print the value between a pair of delimiters, like quotation marks, you could use them in the regular expression:
"([^"]+)" ($1 contains the value)
As long as you develop for a particular PHP version, you can inspect its implementation and assume, that it doesn't change. If you upgrade, you should check, if the assumption is still valid.
A comment in uniqid documentation describes, that it is essentially a hexadecimal number with an optional numeric suffix:
if (more_entropy) {
uniqid = strpprintf(0, "%s%08x%05x%.8F", prefix, sec, usec, php_combined_lcg() * 10);
} else {
uniqid = strpprintf(0, "%s%08x%05x", prefix, sec, usec);
}
Which gives you two possible output formats:
uniqid() - 13 characters, hexadecimal number
uniqid('', true) - 14 - 23 characters, hexadecimal number with floating number suffix
computed elsewhere
If you use other delimiters than alphanumeric characters and dot, you could use one of these simple regular expressions to grab the value in either of the two formats:
[0-9a-f]+
[.0-9a-f]+
If you need 100% format guarantee for any PHP version, you could write your own function based on sprintf.
I admit, that it is unlikely, that the uniqid would significantly change; I would expect creating other extensions to provide different formats. Another comment in uniqid documentation shows a RFC 4211 compliant UUID implementation. There was also a discussion on stackoverflow about it.
I found this on the php site: http://www.php.net/manual/en/function.uniqid.php#95001
If this is to be believed then the 13 character version is entirely hex.
However the 23 character version has:
14 characters (hex)
then a dot
then another 8 characters (decimal)
If you need to be entirely sure, you can verify this yourself: http://sandbox.onlinephpfunctions.com/code/c04c7854b764faee2548180eddb8c23288dcb5f7

How to ensure user submitted numeric input follows a certain format

I am working in PHP and I want to check if a given user submitted numeric input is not too big or too small.
I am storing the amounts in the database as 64 bit integers (multiply them by 10^8 to avoid rounding errors in future calculations). I am limiting the amounts so that they cannot exceed the following precision: no more than 4 numbers after the decimal point. Also since the upper limit on user input should be 99 million, I also want to specify no more than 8 numbers precending the decimal point. How can I achieve this neatly?
My current approach seems a bit hack-ish:
Code:
//Check for existence of decimal point in string using strpos
//explode the string by the decimal point
//do a strlen on both the strings and check they dont exceed 8 and 4 respectively
//if no decimal point, simply do a strelen and check it's not greater than 8
Also, I don't want the inputted data to be smaller than 0.0001. I am proficient in php for web design not familiar with the math functions of php is there an easy way to handle this?
Thanks for any tips
Using the code below you can check whether the $input conforms to your requirements.
(See also this short demo.)
$input = ...;
$pattern = "~^\s*\d{1,8}(?:\.\d{1,4})?\s*$~";
if (($input > 0) && preg_match($pattern, $input)) {
/* $input is OK */
} else {
/* $input is NOT OK */
}
Requirements:
$input has an integral part that is between 1 and 8 digits long.
$input optionally contains a . followed by 1 to 4 digits (fractional part).
$input is a positive number greater than or equal to 0.0001.
You can use regular expressions and the mb_ereg_match(regex,string) function
you can try this:
if (preg_match('~^(?>[1-9]\d{0,7}+|0)(?>\.\d{0,3}+[1,9])?$~', trim($number), $result))
// true
else
// false
This pattern avoids things like 00015 or 1.000, but if you want to allow this, just replace [1-9] by \d or better use ~^\d{1,8}+(?>\.\d{1,4}+)?$~ instead.
You must work after with $result which contain the trimed and verified number.
I think the following regex will match your needs.
It will match all numbers between 99999999.9999 and 00000000.0000 with and without the decimal point and also empty string.
The fractal part contains no more than 4 digits
if (preg_match('~^[0..9]{0,8}(?:\.[0..9]{1,4})?$~'), $number) {
$match = TRUE;
} else{
$match = FALSE;
}
See if this works:
$number = '123.4567';
if(preg_match('/^\s*\d*\.?\d{4}\s*$/', $number)) echo "Match";
else echo "Not match";

Turn zero filled numbers into whole numbers

I have a selection of nubmers which are prefixed with zeros to give them a set length.
0001,0230,1000,0007,0300
How could I make these whole number? So that the resulting numbers are
1,230,1000,7,300
I was using sprintf("%04d", $input); to generate the numbers, is there a reverse of this?
Thanks
If you feel safe that you have numbers and not another kind of string, you can just cast to integer:
(int) $num;
Demo: http://codepad.org/VQrA50fK
Just be aware that a non-integer (like a string with letters) will cast to 0.
Check out the intval function. Just pass them in and they'll be converted to ints. You can then append them to a sting to make them strings again.

Check if a variable is a natural number

I want to make a bid system on a website. That means users can post their bid (natural number). I want to make sure users don't try to post characters, decimal numbers, etc.
I don't want to use is_numeric function because hexadecimal notation is allowed.
I was thinking to use preg_match for this. But in php.net the documentation for this function is little and I have no idea how to use preg_match.
So how should I check if a variable is a natural number with preg_match?
If you don't require decimal points: ctype_digit or filter_var($var, FILTER_VALIDATE_INT).
If you do: filter_var($var, FILTER_VALIDATE_FLOAT).
ctype_digit does what you want:
Checks if all of the characters in the provided string, text, are numerical.
(Before PHP 5.1.0, this function returned TRUE when text was an empty string.)
Either preg_match('/^[0-9]+$/', $var); or ctype_digit
I would generally caution against using regex for parsing numerics, as there are generally better solutions than regex for this, but since you're asking, I'll try to give you some assistance with it:
preg_match uses regular expressions (regex) for it's matching.
You can find out more about regex syntax at sites like http://www.regular-expressions.info/
If you want to match a digit in regex, you can either use [0-9] or \d.
If you want to match one or more of anything, you would use a plus sign.
Finally, regex strings need to be enclosed in a pair of characters. The character chosen is usually a slash (/) character, as some languages specifically require this character, but PHP also allows other characters to be used; tilde (~) is quite common.
So your regex string to match any number of digits would be "/\d+/". This can then be put into a preg_match call like so:
$isnumeric = preg_match("/\d+/",$input_string);
If you have more specific requirements, you can limit the number of characters allowed by replacing the plus sign with {max} or {min,max} where 'min' and 'max' are the number of times the preceding match is allowed. So to allow a number between two and six digits long, you would use this:
$isnumeric = preg_match("/\d{2,6}/",$input_string);
If you need to allow a decimal point, you need to know that the dot character is a special character in regex (it means 'match any character at all'), so you need to escape it with a back-slash.
Therefore, a regex to match a currency amount with two decimal places, and at least one digit before the point would be like this:
$isnumeric = preg_match("/\d+\.\d\d/",$input_string);
Finally, note that regex will return true in all the above if the string simply contains the matched value. To ensure it doesn't contain anything else, you would need to 'anchor' it to the front and end of the string, using the anchor characters: ^ for the start of the string, and $ for the end.
So for the previous example, if you want it to only contain a decimal number, and nothing else, you would need this:
$isnumeric = preg_match("/^\d+\.\d\d$/",$input_string);
Regex is a complex subject, but I hope that gives you a start.
I know this is very old but I wanted to share the next solution in case someone else comes up with this problem.
I'm assuming that by natural number you meant positive integer (which excludes the number 0).
function is_positive_integer( $value ) {
// Check if is integer and greater than zero
if( is_int( $value ) && $value > 0 ) {
return true;
}
// Is not a positive integer
else {
return false;
}
}
This kind of depends on your definition of natural numbers - according to different theories, the number zero (0) does or does not count as a natural number.
To answer your question on how to solve this with preg_match:
If you want to include zero, using preg_match is pretty easy preg_match('^[0-9]+$', $input).
Usage:
if (preg_match('^[0-9]+$', $input))
// $input represents a non-negative numeric value
else
// $input does not represent a non-negative numeric value
If you don't want to include the zero, use preg_match('^[1-9][0-9]*$', $input):
if (preg_match('^[1-9][0-9]*$', $input))
// $input represents a positive numeric value
else
// $input does not represent a positive numeric value
That said - for your particular problem, using ctype_digit is a faster solution, as others already pointed out (you'd have to do a second check if you don't want to allow the number zero).
in_array(str_replace(str_split('0123456789'), '', $s), array(',','.',''));
simple function:
function isnature($x){
$y = ceil($x)-floor($x);
return $y == 0 ? true : false;
}
From a mathematical point of view, a natural number is a positive integer, including zero, so you could check it like this:
is_int($bid) && $bid >= 0
Simplest and faster
if( is_numeric( $key ) && intval( $key ) == $key )
{
//key == number
}

is_numeric or a numeric preg_match?

I read on a forum that you can't completely trust is_numeric(). It lets through "0xFF" for example which is an allowed hexadecimal...
So my question is can you trick is_numeric? Will I need to use a regex to do it correctly?
Here is what is_numeric() considers to be a numeric string:
Numeric strings consist of optional sign, any number of digits, optional decimal part and optional exponential part. Thus +0123.45e6 is a valid numeric value. Hexadecimal notation (0xFF) is allowed too but only without sign, decimal and exponential part.
If you only want to check if a string consists of decimal digits 0-9, you could use ctype_digit().
One can also check using ctype_digit() to check if its a true number.
Regex would obviously be your better option, however it does come with an overhead. So it really depends on your situation and what you want to do.
Is it for validating user input? Then the overhead of using a regexp or asserting it doesn't contain an "x" and is_numeric() wouldn't be too much overhead.
If you just want to check that something is an integer, try this:
function isInteger($value){
return (is_numeric($value) ? intval($value) == $value : false);
}
If you want to check for floats too then this won't work obviously :)

Categories