I'm working on a function to validate a US phone number submitted by a user, which can be submitted in any of the popular number formats people usually use. My code so far is as follows:
$number = '123-456-7890';
function validate_telephone_number($number) {
$formats = array(
'###-###-####',
'(###)###-###',
'(###) ###-###',
'##########'
);
$number = trim(preg_replace('[0-9]', '#', $number));
if (in_array($number, $formats)) {
return true;
} else {
return false;
}
}
First off, this code does not seem to be working, and returns false on all submitted numbers. I can't seem to find my error.
Secondly, I'm looking for an easy way to only allow phone numbers from an array of specific allowed area codes. Any ideas?
For your first question:
preg_replace('/[0-9]/', '#', $number)
or '/\d/'
For the second question this may help you:
$areaCode = substr(preg_replace('/[^\d]/', '', $number),0 , 3);
This will give you the first 3 digits in the number by discarding all other characters.
I'm not familiar with the US area codes format so I cannot help you more with this one.
Bonus:
if (in_array($number, $formats)) {
return true;
} else {
return false;
}
is equivalent to
return in_array($number, $formats);
As a matter of fact any statement of the form
if(<expression>){
return true;
}
else{
return false;
}
can be written as return (bool) <expr>;, but in this case in_array will always return a Boolean so (bool) is not needed.
Your code does not check for well formatted but invalid numbers - for example, no area code starts with 0 or 1 in the US, so this could be checked. Also, your formats do not allow for country code inclusion - +15551234567 would be rejected, for example.
If you don't care about the formatting and just want to validate if the digits in the input amount to a valid US phone number, you could use something like this:
$clean_number = preg_replace("/[^0-9]/", '', $number);
$valid = preg_match("/^(\+?1)?[2-9][0-9]{9}$/", $clean_number);
Of course, this will also accept "foo 5555555555 bar" as a valid number - if you want to disallow that, make the preg_replace more restrictive (e.g, remove only brackets, spaces and dashes).
If you prefer to do this without maintaining a lot of code, you an check out this API that validates a US number and provides several formats for the number https://www.mashape.com/parsify/format
Look here for a code project that has a function for validating phone numbers.
Related
I'm having a problem with my code where NumberFormatter::parse() is truncating decimals when there is a trailing zero. Our app uses Intl to be locale aware, so I am doing a JSON API call to check to make sure a number entered in a form is actually a number. For example when the locale calls for a comma as a decimal separator return FALSE for numbers entered with a period.
function parse_decimals($number)
{
// ignore empty strings and return
if(empty($number))
{
return $number;
}
$config = get_instance()->config;
$fmt = new \NumberFormatter( $config->item('number_locale'), \NumberFormatter::DECIMAL);
$fmt->format(1234567890.12300);
if (empty($config->item('thousands_separator')))
{
$fmt->setAttribute(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL, '');
}
try
{
if(strcmp(strval($fmt->parse($number)),strval($number)) != 0)
{
$number = FALSE;
}
return $fmt->parse($number);
}
catch (Exception $e)
{
return FALSE;
}
}
The locale doesn't matter (because it's happeneing with all locales, so for the sake of argument Intl is used to set the locale to en_US.
Entered 50.00
Expected 50.00
Actual 50
Return Result FALSE
So you can see that parse is truncating 50.00 to 50. I have tried playing around with NumberFormatter::MAX_FRACTION_DIGITS, NumberFormatter::MIN_FRACTION_DIGITS and NumberFormatter::FRACTION_DIGITS without any effect. A side note is that if there is a better way to compare the expected number's format with the entered number's format than this, please feel free to suggest something better than this:
if(strcmp(strval($fmt->parse($number)),strval($number)) != 0)
{
$number = FALSE;
}
I have two variables in a PHP program for billing statements, $charges and $payments.
$charges is the total amount due before any payments. $payments is the total amount received.
I calculate the balance due like so:
$balance_due = $charges-$payments;
Simple, except I am getting the following result:
$balance_due has -9.0949470177293E-13 for a value (expecting 0).
Both $charges and $payments have a value of 5511.53.
When I var_dump($charges) and var_dump($payments) they both show: float(5511.53)
This code (and === ):
if($charges == $payments){
error_log('they are the same');
}else{
error_log('they are not the same');
}
both result in false.
If I hard code: $charges = $payments = 5511.53; and run it then $balance_due = 0 as expected.
I am confused. What am I missing?
EDIT NOTES
I was able to use a user contributed function by Nitrogen found on the BC Math Functions page that was suggested I look at in order to come up with the following solution:
if(Comp($charges, $payments)===0){
$balance_due = 0;
}else{
$balance_due = ( $charges - $payments );
}
function Comp($Num1,$Num2,$Scale=null) {
// check if they're valid positive numbers, extract the whole numbers and decimals
if(!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num1,$Tmp1)||
!preg_match("/^\+?(\d+)(\.\d+)?$/",$Num2,$Tmp2)) return('0');
// remove leading zeroes from whole numbers
$Num1=ltrim($Tmp1[1],'0');
$Num2=ltrim($Tmp2[1],'0');
// first, we can just check the lengths of the numbers, this can help save processing time
// if $Num1 is longer than $Num2, return 1.. vice versa with the next step.
if(strlen($Num1)>strlen($Num2)) return(1);
else {
if(strlen($Num1)<strlen($Num2)) return(-1);
// if the two numbers are of equal length, we check digit-by-digit
else {
// remove ending zeroes from decimals and remove point
$Dec1=isset($Tmp1[2])?rtrim(substr($Tmp1[2],1),'0'):'';
$Dec2=isset($Tmp2[2])?rtrim(substr($Tmp2[2],1),'0'):'';
// if the user defined $Scale, then make sure we use that only
if($Scale!=null) {
$Dec1=substr($Dec1,0,$Scale);
$Dec2=substr($Dec2,0,$Scale);
}
// calculate the longest length of decimals
$DLen=max(strlen($Dec1),strlen($Dec2));
// append the padded decimals onto the end of the whole numbers
$Num1.=str_pad($Dec1,$DLen,'0');
$Num2.=str_pad($Dec2,$DLen,'0');
// check digit-by-digit, if they have a difference, return 1 or -1 (greater/lower than)
for($i=0;$i<strlen($Num1);$i++) {
if((int)$Num1{$i}>(int)$Num2{$i}) return(1);
else
if((int)$Num1{$i}<(int)$Num2{$i}) return(-1);
}
// if the two numbers have no difference (they're the same).. return 0
return(0);
}
}
}
That solution worked for me. The answer provided by imtheman below also works and seems more efficient so I am going to use that one instead. Is there any reason not to use one or the other of these?
The way I solved this problem when I ran into it was using php's number_format(). From php documentation:
string number_format(float $number [, int $decimals = 0 ])
So what I would do is this:
$balance_due = number_format($charges-$payments, 2);
And that should solve your problem.
Note: number_format() will return a string, so to compare it you must use == (not ===) or cast it back into a (float) before comparison.
function sendSms($toPhone,$message){
$toPhone=intval(trim($toPhone));
if(strlen($toPhone)== 8 && $toPhone{0}==9){
//sending sms
}else{
return "error";
}
}
I am trying to validate mobile numbers for sending SMS. The first line trims the phone number string and then converts it to an integer. In the if statement, I want to make sure that the number length is 8 digits and it begins with 9. This function always goes for the else even if the number is correct( 8 digits and begins with 9). What could be the issue here.
Why not regex?
$valid = preg_match('/^9[0-9]{7}$/', trim($phone));
You can remove from $toPhone all not digits
function sendSms($toPhone,$message){
$_phone = '';
for ($i = 0; $i < strlen($toPhone); $i++)
{
if (is_numeric($toPhone[$i]))
$_phone .= $toPhone[$i];
}
if(strlen($_phone)== 8 && $_phone[0]=='9'){
//sending sms
}else{
return "error";
}
}
After you converted the phone number to an integer with $toPhone=intval(trim($toPhone));,, you can't access the digits in the way you are trying with $toPhone{0}, because you operate on a number and not on a string any more.
See this isolated example:
$number = 987654321;
var_dump($number{0}); //NULL
However, substr would be capable of doing this:
$number = 987654321;
var_dump(substr($number, 0, 1)); //string(1) "9"
Converting a whole number to integer isn't a good idea anyways, because users might enter the number with spaces in between or signs like + and /. Better search for an already existing approach to validate phone numbers.
Take a look here, where the topic "validate mobile phone numbers" is covered in more detail: A comprehensive regex for phone number validation
You convert variable to integer and apparently $toPhone[0] works on strings only.
The same function without intval() works as you wanted.
function sendSms($toPhone, $message)
{
$toPhone = trim($toPhone);
if(strlen($toPhone) == 8 && $toPhone[0] == 9){
//sending sms
} else {
return "error";
}
}
I'm trying to validate an input for Account number in php form. It should contain 8 numbers and '-' optionally. If there is '-' - it should be ignored.
After pressing the Submit button, the warning message suppose to be displayed above the form in case input is invalid.
Please help.
This is what I got so far, but I'm not sure if this is correct and don't know how to display a warning message above the form.
$acctnum= "$acctnum";
if(empty($acctnum)){
echo "You did not enter an account number, please re-enter"; }
else if(!preg_match("\-^[0-9]{8}", $acctnum)){
echo "Your account number can only contain eight numbers. Please re-enter."; }
Thank you!
You don't appear to be trying. No documentation or tutorial will tell you to make a Regex like that. For starters, where are the delimiters? Why is - escaped when it's outside a character class and therefore has no special meaning? What is that ^ doing there?
This should do it:
$acctnum = str_replace("-","",$acctnum);
if( !preg_match("/^\d{8}$/",$acctnum)) echo "Error...";
Since regex are quite expensive I'd go like that instead:
$acctnum = (int) $acctnum; // this automatically ignore the '-'
if ($acctnum < 0) $acctnum = -$acctnum;
$digits = ($acctnum == 0) ? log10($acctnum) + 1 : 1;
if ($digits === 8) { ... }
Split the task in two. First get rid of the "-" with str_replace and then check for the numbers.
$match = preg_match("/^\d{8}$/", str_replace("_", "", $str));
if ($match > 0) {
// Correct
} else {
// incorrect
}
I am trying to validate a phone number and require it to have 10 digits only no spaces or special characters allowed (example: 0123456789) and the same goes with zip code except 5 digits only (example: 01234).
This is what I have for the phone number field so far.
$phone = stripslashes($_POST['phone']);
if(!$phone || $phone == "Phone Number*")
{
$error .= "Please enter your phone number.<br />";
}
The next if statement should retrieve an error similar to "Please enter a valid phone number. Example: "0123456789".
If you don't want to use regular expressions, take a look at ctype_digit
For example:
if(strlen($phone)==10 && ctype_digit($phone)) {
//valid
} else {
//invalid
}
I can't testify to whether this will be faster or slower than regular expressions, but I would reckon it's probably moot. It's more or less what makes the most sense to you.
You can try regex here:
if(preg_match('/^[0-9]{10}$/', $phone)){
// valid
}else{
// Not valid
}
Something a little like that will ensure only numerical characters and 10 of them. Just change the 10 to 5 for zip code.
One more thing if $_POST['phone'] is not set when you access it you will get a E_NOTICE so just a tip here for you do:
$phone = isset($_POST['phone']) ? stripslashes($_POST['phone']) : null;
if(!$phone) // ERROR
$phone = stripslashes($_POST['phone']);
if(!$phone || $phone == "Phone Number*")
{
$error .= "Please enter your phone number.<br />";
}
if(!preg_match('/^\d{10}$/', $phone)) $error .= "Please enter phone number as ##########.<br />";
And for zip code
if(!preg_match('/^\d{5}$/', $zip)) $error .= "Please enter your zip code as #####.<br />";
Keep in mind that this will not allow foreign zip codes (which may be of different lengths or include letters)
Just some other suggestions too (to prevent unnecessary error messages)
You may want to process your user input such that 123-456-7890 becomes 1234567890 by doing something like
preg_replace('/[^\d]/','',$input)
Maybe do a trim($input) to strip leading/trailing whitespace
Finally, is there any particular reason you are using stripslashes on $_POST['phone']?
If they are all digits like you expect, then this shouldnt be necessary.
If they aren't all digits, then you will throw an error regardless
how about:
function check($number,$length)
{
if(ctype_digit ($number) && strlen($number)==$length)
return true;
else
return false;
}
if(check("1234",4))
echo "ok";
else
echo "Please enter a valid phone number. Example: "0123456789";
Well, this an old post but I will throw in some comments here anyway.
1) you should really not force the user to put in the right numbers, of course your validation on the front end will cover this but never assume it to be case coming into the "backend" .
Consider the following instead of putting the on the user:
// remove chars
$number = preg_replace('/[\D]/', '', $number);
//unit test sanitizer
filter_var($number, FILTER_SANITIZE_NUMBER_INT)
// check number
preg_match('/^[0-9]{10}$/', $zip)
Example : Read in user input if enough digits entered in look up closest matching zipcode etc.. (I actually used this on a site once) Of course setting the frontend to check is useful, but in case that fails .
$number = 'z 02012s';
// remove chars
$number = preg_replace('/[\D]/', '', $number);
//unit test sanitizer
$number = filter_var($number, FILTER_SANITIZE_NUMBER_INT);
// check number
if (preg_match('#^[0-9]{5}$#', $number) === 1) {
//(optional) lookup closest zip using your DB.
$look_zip = $db->getClosestZipMatch($number);
} else {
echo $number . " isn't 5 digits only, do something.";
}