Validate TelephoneNumber PHP - php

I was given a task to validate a telephone number (stored in the var $number) introduced by a user in my website
$number = $_POST["telephone"];
The thing is this validation is quite complex as i must validate the number to see if it is from Portugal. i thought about validating it using all the indicators from Portugal, which are 52: (50 indicators are 3 digits long and 2 indicators are 2 digits long) Example of a number:
254872272 (254 is the indicator)
i also thought about making an array with all the indicators and then with a cycle verificate somehow if the first 2/3 digits are equal to the ones in the array.
what do you guys think? how should i solve this problem?

One way is to use regular expressions with named subpatterns:
$number = 254872272;
$ind = array( 251, 252, 254 );
preg_match( '/^(?<ind>\d{3})(?<rest>\d{6})$/', $number, $match );
if ( isset($match['ind']) && in_array( (int) $match['ind'], $ind, true ) ) {
print_r( $match );
/*
Array
(
[0] => 254872272
[ind] => 254
[1] => 254
[rest] => 872272
[2] => 872272
)
*/
}
Or you can insert indicators directly into regular expression:
preg_match( '/^(?<ind>251|252|254)(?<rest>\d{6})$/', $number, $match );

There's potential REGEX ways of "solving" this, but really, all you need is in_array() with your indicators in an array. For example:
$indicators = array('254', '072', '345');
$numbers = array(
'254872272',
'225872272',
'054872272',
'072872272',
'294872272',
'974872272',
'345872272'
);
while ($number = array_shift($numbers)) {
$indicator = substr($number, 0, 3);
if (in_array($indicator, $indicators)) {
echo "$number is indicated ($indicator).\n";
} else {
echo "$number is NOT indicated ($indicator).\n";
}
}
http://codepad.org/zesUaxF7
This gives:
254872272 is indicated (254).
225872272 is NOT indicated (225).
054872272 is NOT indicated (054).
072872272 is indicated (072).
294872272 is NOT indicated (294).
974872272 is NOT indicated (974).
345872272 is indicated (345).
Also, I use strings instead of integers on purpose, since PHP is going to interpret any numbers that begin with 0 (like 0724445555) as not having a leading zero, so you need to use a string to make sure that works correctly.

Perhaps with a regular expression?
I have not tested the following, but it should check for one of the matching indicators, followed by any 6 digits, something like:
$indicators = array('123' ,'456', '78'); // etc...
$regex = '/^(' . implode('|', $indicators) . ')[0-9]{6}$/';
if(preg_match($regex, 'your test number')) {
// Run further code...
}

There's a couple of libraries around that aim to validate as many telephone number formats as possible against the actual validation format, as defined by the relevant authorities.
They are usually based on a library by Google, and there are versions for PHP.

Related

How to effectively convert positive number in base -2 to a negative one in base - 2?

Old question name: How to effectively split a binary string in a groups of 10, 0, 11?
I have some strings as an input, which are binary representation of a number.
For example:
10011
100111
0111111
11111011101
I need to split these strings (or arrays) into groups of 10, 0, and 11 in order to replace them.
10 => 11
0 => 0
11 => 10
How to do it? I have tried these options but don't work.
preg_match('/([10]{2})(0{1})([11]{2})/', $S, $matches);
It should be [10] [0], [11] for 10011 input.
And it should be 11010 when replaced.
UPD1.
Actually, I'm trying to do a negation algorithm for converting a positive number in a base -2 to a negative one in a base -2.
It could be done with an algorithm from Wikipedia with a loop. But byte groups replacing is a much faster. I have implemented it already and just trying to optimize it.
For this case 0111111 it's possible to add 0 in the end. Then rules will be applied. And we could remove leading zeros in a result. The output will be 101010.
UPD2.
#Wiktor Stribiżew proposed an idea how to do a replace immediately, without splitting bytes into groups first.
But I have a faster solution already.
$S = strtr($S, $rules);
The meaning of this question isn't do a replacement, but get an array of desired groups [11] [0] [10].
UPD3.
This is a solution which I reached with an idea of converting binary groups. It's faster than one with a loop.
function solution2($A)
{
$S = implode('', $A);
//we could add leading 0
if (substr($S, strlen($S) - 1, 1) == 1) {
$S .= '0';
}
$rules = [
'10' => '11',
'0' => '0',
'11' => '10',
];
$S = strtr($S, $rules);
$arr = str_split($S);
//remove leading 0
while ($arr[count($arr) - 1] == 0) {
array_pop($arr);
}
return $arr;
}
But the solution in #Alex Blex answer is faster.
You may use a simple /11|10/ regex with a preg_replace_callback:
$s = '10011';
echo preg_replace_callback("/11|10/", function($m) {
return $m[0] == "11" ? "10" : "11"; // if 11 is matched, replace with 10 or vice versa
}, $s);
// => 11010
See the online PHP demo.
Answering the question
algorithm for converting a positive number in a base -2 to a negative one in a base -2
I believe following function is more efficient than a regex:
function negate($negabin)
{
$mask = 0xAAAAAAAAAAAAAAA;
return decbin((($mask<<1)-($mask^bindec($negabin)))^$mask);
}
Parameter is a positive int60 in a base -2 notation, e.g. 11111011101.
The function converts the parameter to base 10, negate it, and convert it back to base -2 as described in the wiki: https://en.wikipedia.org/wiki/Negative_base#To_negabinary
Works on 64bit system, but can easily adopted to work on 32bit.

preg_match admits only two consecutive lowercases

I want to check if password contains:
minimum 2 lower cases
minimum 1 upper case
minimum 2 selected special characters
The problem is that when i want to verify this,it admits two lowercases,but only if they are consecutive,like this:paSWORD .
if I enter pASWORd,it returns an error.
This is the code
preg_match("/^(?=.*[a-z]{2})(?=.*[A-Z])(?=.*[_|!|#|#|$|%|^|&|*]{2}).+$/")
I don't see where the problem is and how to fix it.
You're looking for [a-z]{2} in your regex. That is two consecutive lowercases!
I will go out on a limb and suggest that it is probably better to individually check each of your three conditions in separate regexes rather than trying to be clever and do it in one.
I've put some extra braces in which may get your original idea to work for non-consecutive lowercase/special chars, but I think the expression is overcomplex.
preg_match("/^(?=(.*[a-z]){2})(?=.*[A-Z])(?=(.*[_!##$%^&*]){2}).+$/")
You can use this pattern to check the three rules:
preg_match("/(?=.*[a-z].*[a-z])(?=.*[A-Z])(?=.*[_!##$%^&*].*[_!##$%^&*])/");
but if you want to allow only letters and these special characters, you must add:
preg_match("/^(?=.*[a-z].*[a-z])(?=.*[A-Z])(?=.*[_!##$%^&*].*[_!##$%^&*])[a-zA-Z_!##%^&*]+$/");
a way without regex
$str = '*MauriceAimeLeJambon*';
$chars = 'abcdefghijklmnopqrtuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_!##$%^&*';
$state = array('lower' => 2, 'upper' => 1, 'special' => 2);
$strlength = strlen($str);
for ($i=0; $i<$strlength; $i++) {
$pos = strpos($chars, $str[$i]);
if (is_numeric($pos)) {
if ($state['lower'] && $pos<26) $state['lower']--;
elseif ($state['upper'] && $pos<52) $state['upper']--;
elseif ($state['special']) $state['special']--;
} else { $res = false; break; }
$res = !$state['lower'] && !$state['upper'] && !$state['special'];
}
var_dump($res);
(This version give the same result than the second pattern. If you want the same result than the first pattern, just remove the else {} and put the last line out of the for loop.)

Splitting address strings on Sequential-Number strings (2nd, 8th, 3rd, first, etc..)

I've been tasked with standardizing some address information. Toward that goal, I'm breaking the address string into granular values (our address schema is very similar to Google's format).
Progress so far:
I'm using PHP, and am currently breaking out Bldg, Suite, Room#, etc... info.
It was all going great until I encountered Floors.
For the most part, the floor info is represented as "Floor 10" or "Floor 86". Nice & easy.
For everything to that point, I can simply break the string on a string ("room", "floor", etc..)
The problem:
But then I noticed something in my test dataset. There are some cases where the floor is represented more like "2nd Floor".
This made me realize that I need to prepare for a whole slew of variations for the FLOOR info.
There are options like "3rd Floor", "22nd floor", and "1ST FLOOR". Then what about spelled out variants such as "Twelfth Floor"?
Man!! This can become a mess pretty quickly.
My Goal:
I'm hoping someone knows of a library or something that already solves this problem.
In reality, though, I'd be more than happy with some good suggestions/guidance on how one might elegantly handle splitting the strings on such diverse criteria (taking care to avoid false positives such as "3rd St").
first of all, you need to have exhaustive list of all possible formats of the input and decide, how deep you'd like to go.
If you consider spelled out variants as invalid case, you may apply simple regular expressions to capture number and detect the token (room, floor ...)
I would start by reading up on regex in PHP. For example:
$floorarray = preg_split("/\sfloor\s/i", $floorstring)
Other useful functions are preg_grep, preg_match, etc
Edit: added a more complete solution.
This solution takes as an input a string describing the floor. It can be of various formats such as:
Floor 102
Floor One-hundred two
Floor One hundred and two
One-hundred second floor
102nd floor
102ND FLOOR
etc
Until I can look at an example input file, I am just guessing from your post that this will be adequate.
<?php
$errorLog = 'error-log.txt'; // a file to catalog bad entries with bad floors
// These are a few example inputs
$addressArray = array('Fifty-second Floor', 'somefloor', '54th floor', '52qd floor',
'forty forty second floor', 'five nineteen hundredth floor', 'floor fifty-sixth second ninth');
foreach ($addressArray as $id => $address) {
$floor = parseFloor($id, $address);
if ( empty($floor) ) {
error_log('Entry '.$id.' is invalid: '.$address."\n", 3, $errorLog);
} else {
echo 'Entry '.$id.' is on floor '.$floor."\n";
}
}
function parseFloor($id, $address)
{
$floorString = implode(preg_split('/(^|\s)floor($|\s)/i', $address));
if ( preg_match('/(^|^\s)(\d+)(st|nd|rd|th)*($|\s$)/i', $floorString, $matchArray) ) {
// floorString contained a valid numerical floor
$floor = $matchArray[2];
} elseif ( ($floor = word2num($floorString)) != FALSE ) { // note assignment op not comparison
// floorString contained a valid english ordinal for a floor
; // No need to do anything
} else {
// floorString did not contain a properly formed floor
$floor = FALSE;
}
return $floor;
}
function word2num( $inputString )
{
$cards = array('zero',
'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten',
'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen', 'twenty');
$cards[30] = 'thirty'; $cards[40] = 'forty'; $cards[50] = 'fifty'; $cards[60] = 'sixty';
$cards[70] = 'seventy'; $cards[80] = 'eighty'; $cards[90] = 'ninety'; $cards[100] = 'hundred';
$ords = array('zeroth',
'first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth', 'tenth',
'eleventh', 'twelfth', 'thirteenth', 'fourteenth', 'fifteenth', 'sixteenth', 'seventeenth', 'eighteenth', 'nineteenth', 'twentieth');
$ords[30] = 'thirtieth'; $ords[40] = 'fortieth'; $ords[50] = 'fiftieth'; $ords[60] = 'sixtieth';
$ords[70] = 'seventieth'; $ords[80] = 'eightieth'; $ords[90] = 'ninetieth'; $ords[100] = 'hundredth';
// break the string at any whitespace, dash, comma, or the word 'and'
$words = preg_split( '/([\s-,](?!and\s)|\sand\s)/i', $inputString );
$sum = 0;
foreach ($words as $word) {
$word = strtolower($word);
$value = array_search($word, $ords); // try the ordinal words
if (!$value) { $value = array_search($word, $cards); } // try the cardinal words
if (!$value) {
// if temp is still false, it's not a known number word, fail and exit
return FALSE;
}
if ($value == 100) { $sum *= 100; }
else { $sum += $value; }
}
return $sum;
}
?>
In the general case, parsing words into numbers is not easy. The best thread that I could find that discusses this is here. It is not nearly as easy as the inverse problem of converting numbers into words. My solution only works for numbers <2000, and it liberally interprets poorly formed constructs rather than tossing an error. Also, it is not resilient against spelling mistakes at all. For example:
forty forty second = 82
five nineteen hundredth = 2400
fifty-sixth second ninth = 67
If you have a lot of inputs and most of them are well formed, throwing errors for spelling mistakes is not really a big deal because you can manually correct the short list of problem entries. Silently accepting bad input, however, could be a real problem depending on your application. Just something to think about when deciding if it is worth it to make the conversion code more robust.

Printing all permutations of strings that can be formed from phone number

I'm trying to print the possible words that can be formed from a phone number in php. My general strategy is to map each digit to an array of possible characters. I then iterate through each number, recursively calling the function to iterate over each possible character.
Here's what my code looks like so far, but it's not working out just yet. Any syntax corrections I can make to get it to work?
$pad = array(
array('0'), array('1'), array('abc'), array('def'), array('ghi'),
array('jkl'), array('mno'), array('pqr'), array('stuv'), array('wxyz')
);
function convertNumberToAlpha($number, $next, $alpha){
global $pad;
for($i =0; $i<count($pad[$number[$next]][0]); $i++){
$alpha[$next] = $pad[$next][0][$i];
if($i<strlen($number) -1){
convertNumberToAlpha($number, $next++, $alpha);
}else{
print_r($alpha);
}
}
}
$alpha = array();
convertNumberToAlpha('22', 0, $alpha);
How is this going to be used? This is not a job for a simple recursive algorithm such as what you have suggested, nor even an iterative approach. An average 10-digit number will yield 59,049 (3^10) possibilities, each of which will have to be evaluated against a dictionary if you want to determine actual words.
Many times, the best approach to this is to pre-compile a dictionary which maps 10-digit numbers to various words. Then, your look-up is a constant O(1) algorithm, just selecting by a 10 digit number which is mapped to an array of possible words.
In fact, pre-compiled dictionaries were the way that T9 worked, mapping dictionaries to trees with logarithmic look-up functions.
The following code should do it. Fairly straight forward: it uses recursion, each level processes one character of input, a copy of current combination is built/passed at each recursive call, recursion stops at the level where last character of input is processed.
function alphaGenerator($input, &$output, $current = "") {
static $lookup = array(
1 => "1", 2 => "abc", 3 => "def",
4 => "ghi", 5 => "jkl", 6 => "mno",
7 => "pqrs", 8 => "tuv", 9 => "wxyz",
0 => "0"
);
$digit = substr($input, 0, 1); // e.g. "4"
$other = substr($input, 1); // e.g. "3556"
$chars = str_split($lookup[$digit], 1); // e.g. "ghi"
foreach ($chars as $char) { // e.g. g, h, i
if ($other === false) { // base case
$output[] = $current . $char;
} else { // recursive case
alphaGenerator($other, $output, $current . $char);
}
}
}
$output = array();
alphaGenerator("43556", $output);
var_dump($output);
Output:
array(243) {
[0]=>string(5) "gdjjm"
[1]=>string(5) "gdjjn"
...
[133]=>string(5) "helln"
[134]=>string(5) "hello"
[135]=>string(5) "hfjjm"
...
[241]=>string(5) "iflln"
[242]=>string(5) "ifllo"
}
You should read Norvigs article on writing a spellchecker in Python http://norvig.com/spell-correct.html . Although its a spellchecker and in python not php, it is the same concept around finding words with possible variations, might give u some good ideas.

Whats the cleanest way to convert a 5-7 digit number into xxx/xxx/xxx format in php?

I have sets of 5, 6 and 7 digit numbers. I need them to be displayed in the 000/000/000 format. So for example:
12345 would be displayed as 000/012/345
and
9876543 would be displayed as 009/876/543
I know how to do this in a messy way, involving a series of if/else statements, and strlen functions, but there has to be a cleaner way involving regex that Im not seeing.
sprintf and modulo is one option
function formatMyNumber($num)
{
return sprintf('%03d/%03d/%03d',
$num / 1000000,
($num / 1000) % 1000,
$num % 1000);
}
$padded = str_pad($number, 9, '0', STR_PAD_LEFT);
$split = str_split($padded, 3);
$formatted = implode('/', $split);
You asked for a regex solution, and I love playing with them, so here is a regex solution!
I show it for educational (and fun) purpose only, just use Adam's solution, clean, readable and fast.
function FormatWithSlashes($number)
{
return substr(preg_replace('/(\d{3})?(\d{3})?(\d{3})$/', '$1/$2/$3',
'0000' . $number),
-11, 11);
}
$numbers = Array(12345, 345678, 9876543);
foreach ($numbers as $val)
{
$r = FormatWithSlashes($val);
echo "<p>$r</p>";
}
OK, people are throwing stuff out, so I will too!
number_format would be great, because it accepts a thousands separator, but it doesn't do padding zeroes like sprintf and the like. So here's what I came up with for a one-liner:
function fmt($x) {
return substr(number_format($x+1000000000, 0, ".", "/"), 2);
}
Minor improvement to PhiLho's suggestion:
You can avoid the substr by changing the regex to:
function FormatWithSlashes($number)
{
return preg_replace('/^0*(\d{3})(\d{3})(\d{3})$/', '$1/$2/$3',
'0000' . $number);
}
I also removed the ? after each of the first two capture groups because, when given a 5, 6, or 7 digit number (as specified in the question), this will always have at least 9 digits to work with. If you want to guard against the possibility of receiving a smaller input number, run the regex against '000000000' . $number instead.
Alternately, you could use
substr('0000' . $number, -9, 9);
and then splice the slashes in at the appropriate places with substr_replace, which I suspect may be the fastest way to do this (no need to run regexes or do division), but that's really just getting into pointless optimization, as any of the solutions presented will still be much faster than establishing a network connection to the server.
This would be how I would write it if using Perl 5.10 .
use 5.010;
sub myformat(_;$){
# prepend with zeros
my $_ = 0 x ( 9-length($_[0]) ) . $_[0];
my $join = $_[1] // '/'; # using the 'defined or' operator `//`
# m// in a list context returns ($1,$2,$3,...)
join $join, m/ ^ (\d{3}) (\d{3}) (\d{3}) $ /x;
}
Tested with:
$_ = 11111;
say myformat;
say myformat(2222);
say myformat(33333,';');
say $_;
returns:
000/011/111
000/002/222
000;033;333
11111
Back-ported to Perl 5.8 :
sub myformat(;$$){
local $_ = #_ ? $_[0] : $_
# prepend with zeros
$_ = 0 x ( 9-length($_) ) . $_;
my $join = defined($_[1]) ? $_[1] :'/';
# m// in a list context returns ($1,$2,$3,...)
join $join, m/ ^ (\d{3}) (\d{3}) (\d{3}) $ /x;
}
Here's how I'd do it in python (sorry I don't know PHP as well). I'm sure you can convert it.
def convert(num): #num is an integer
a = str(num)
s = "0"*(9-len(a)) + a
return "%s/%s/%s" % (s[:3], s[3:6], s[6:9])
This just pads the number to have length 9, then splits the substrings.
That being said, it seems the modulo answer is a bit better.

Categories