Page 1 of 1

Checking characters from an array of inputs.

Posted: Mon Mar 13, 2006 5:33 am
by keveh
I've had a look around these forums and php.net, but I haven't managed to find a simple solution to my problem.

I have created a very basic form, and all I want to do is check all of the inputs have characters a-z and 0-9, so that nobody can try anything funny by using < ' etc etc.

So say i wanted to check: firstname, surname, houseno, address, country, telephone number

for values: a-z, A-Z, 0-9

Thanks

Posted: Mon Mar 13, 2006 5:51 am
by shiznatix
somthing like this should work

Code: Select all

$string = preg_replace('/\W+/', '', $string);
this will strip out everything but numbers, letters, spaces, and underscores.

Posted: Mon Mar 13, 2006 5:53 am
by matthijs
Or ctype_alnum which returns true for letters and numbers

Code: Select all

if (ctype_alnum($value) {
		    return TRUE;
} else {
		    return FALSE;
}

Posted: Mon Mar 13, 2006 5:54 am
by phpScott
d11wtq created a great validation class
that did most of if it but missed the alpha numeric bit so I added it to my copy.

Code: Select all

function isAlphaNumeric($input) {
    	if(preg_match('/^[a-zA-Z0-9]*$/', $input)) return true;
    	return false;
    }//End isAlphaNumeric
if this is wrong one of the great reger's around here will correct it i'm sure

Posted: Mon Mar 13, 2006 8:09 am
by keveh
I've had a go at this, but I'm not getting anywhere.

I've put a count in, so that if there is 1 or more errors somewhere a user is told no.

Will any of the above solutions make this simpler and easier? Oh yeah, and work?

Code: Select all

$strings = array($title, $firsname, $middle, $surname, $address1, $address2, $city);
	$error_count = 0;

	foreach($strings as $value){
		if(eregi('/[a-zA-Z0-9][]/', $value)){
			//nothing
		} else { 
			$error_count++;
		}
	}

	if ($error_count > 0){
		echo('no');
	} else {
		echo('yes');
	}
This doesn't seem to work, there is a :space: in there, but it hasn't been picked up

Posted: Mon Mar 13, 2006 8:54 am
by matthijs

Code: Select all

foreach($strings as $value){
        if(ctype_alnum($value)) {
            //nothing
        } else { 
            $error_count++;
        }
    }

    if ($error_count > 0){
        echo('no');
    } else {
        echo('yes');
    }
or

Code: Select all

foreach($strings as $value){
        if(ereg("[^a-zA-Z0-9]", $value)){
            $error_count++;
        }
    }
The caret ^operator reverses the selection. So if value contains anything other then a-zA-Z0-9 then ereg returns true (1). You can add more characters to this if you want. Like spaces, underscores, etc.

Posted: Mon Mar 13, 2006 9:25 am
by keveh
matthijs wrote:

Code: Select all

foreach($strings as $value){
        if(ereg("[^a-zA-Z0-9]", $value)){
            $error_count++;
        }
    }
The caret ^operator reverses the selection. So if value contains anything other then a-zA-Z0-9 then ereg returns true (1). You can add more characters to this if you want. Like spaces, underscores, etc.
That works a treat, thanks!

Posted: Mon Mar 13, 2006 10:41 am
by matthijs
You can also use preg_match. I believe preg_match is faster

Code: Select all

foreach($strings as $value){
        //if(ctype_alnum($value)) {
        //if(ereg("[^a-zA-Z0-9]", $value)){
        if(preg_match("/[^a-zA-Z0-9]/", $value)){
            $error_count++;
        }
    }
and safer (according to Ilia Alshanetsky in PHP architects guide to php security). Try the example from that book:

Code: Select all

<?php
if(ereg("[^-'A-Za-z0-9 \t\n]", "don't forget about \n secu-rity\0\\") ) 
{ 
   echo 'Ereg returns true<br>';
}
else 
{
 echo 'Ereg returns false<br>';
}

if( preg_match("![^-'A-Za-z \t\n]!", "don't forget about \n secu-rity\0\\") ) 
{ 
   echo 'Preg_match returns true<br>'; 
}
else 
{
 echo 'Preg_match returns false<br>';
}
?>

Posted: Mon Mar 13, 2006 11:55 am
by feyd
yes, preg_* is faster than ereg, et al.

Posted: Mon Mar 13, 2006 2:53 pm
by John Cartwright
ctype_alnum() is in all likelyhood faster than preg_*

Posted: Mon Mar 13, 2006 6:57 pm
by feyd
Jcart wrote:ctype_alnum() is in all likelyhood faster than preg_*
Let's find out.

Code: Select all

<?php

$iterations = 10000;

$tests = array(
	'abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq' => true,
	'This is exactly 64 bytes long, not counting the terminating byte' => false
);

$calls = array(
	array('preg_match','#^[a-z0-9]*$#i'),
	array('preg_match','#^[a-z0-9]+$#i'),
	array('preg_match','#^[a-zA-Z0-9]*$#'),
	array('preg_match','#^[A-z0-9]*$#'),
	array('eregi','^[a-z0-9]*$'),
	array('eregi','^[a-z0-9]+$'),
	array('ereg','^[a-zA-Z0-9]*$'),
	array('ereg','^[A-z0-9]*$'),
	array('ctype_alnum'),
);

if(function_exists('bcadd'))
{
	bcscale(20); // precision
	function addTime(&$a,$b,$c)
	{
		$b = explode(' ', $b);
		$c = explode(' ', $c);
		$a = bcadd($a, bcadd(bcsub($c[0], $b[0]), bcsub($c[1], $b[1])));
	}
	
	function div($a, $b)
	{
		return bcdiv($a, $b);
	}
}
else
{
	function addTime(&$a,$b,$c)
	{
		$b = explode(' ', $b);
		$c = explode(' ', $c);
		$a += $c[0] - $b[0] + $c[1] - $b[1];
	}
	
	function div($a, $b)
	{
		return $a / $b;
	}
}

function rendNumber($a)
{
	$out = strval($a);
	if(strpos('.',$out) !== false)
	{
		$out = rtrim($out, '.0');
	}
	
	if(func_num_args() > 1)
	{
		$arg = func_get_arg(1);
		$out = number_format($out, $arg);
	}
	else
	{
		$out = number_format($out);
	}
	
	return $out;
}

function flash($var)
{
	if($var === null)
	{
		return 'null';
	}
	elseif(is_array($var))
	{
		$c = 0;
		$o = 'array(';
		foreach($var as $k => $v)
		{
			$o .= ($c > 0 ? ',' : '') . ($k == $c ? '' : flash($k) . '=>') . flash($v);
			$c++;
		}
		$o .= ')';
		return $o;
	}
	elseif(is_scalar($var))
	{
		return var_export($var,true);
	}
	elseif(is_object($var))
	{
		return 'object ' . get_class($var);
	}
	elseif(is_resource($var))
	{
		return 'reference ' . get_resource_type($var);
	}
	else
	{
		return 'unknown';
	}
}


$p = 8;	//	precision
$results = array();
foreach($tests as $test => $comp)
{
	foreach($calls as $call)
	{
		$time = 0;
		$func = array_shift($call);
		array_push($call, $test);
		for($i = 0; $i < $iterations; $i++)
		{
			$t1 = microtime();
			$result = call_user_func_array($func, $call);
			$t2 = microtime();
			addTime($time, $t1, $t2);
		}
		$code = preg_replace('#^array(?=\()#i', $func, flash($call,true));
		$pass = ($result == $comp ? 'PASS' : 'FAIL');
		$result = flash($result);
		array_push($results, array('code' => $code, 'result' => $result, 'pass' => $pass, 'time' => rendNumber($time, $p), 'avg' => rendNumber(div($time,$iterations), $p)));
	}
}

$l = 0;
$c = strlen('Code');
$r = strlen('Result');
$p = strlen('Pass');
$t = strlen('Time');
$a = strlen('Average');
foreach($results as $result)
{
	foreach($result as $key => $elem)
	{
		$$key = strlen($elem);
		${$key{0}} = max($$key, ${$key{0}});
	}
	$l = max(2 + $code + 3 + $result + 3 + $pass + 3 + $time + 3 + $avg + 2, $l);
}
$line = '+' . str_repeat('-', $c + 2) . '+' . str_repeat('-', $r + 2) . '+' . str_repeat('-', $p + 2) . '+' . str_repeat('-', $t + 2) . '+' . str_repeat('-', $a + 2) . '+' . PHP_EOL;

echo rendNumber($iterations) . ' interation' . ($iterations != 1 ? 's' : '') . PHP_EOL;
echo $line;
printf("| %-{$c}s | %-{$r}s | %-{$p}s | %-{$t}s | %-{$a}s |" . PHP_EOL, 'Code', 'Result', 'Pass', 'Time', 'Average');
echo $line;
foreach($results as $result)
{
	printf("| %-{$c}s | %{$r}s | %{$p}s | %{$t}s | %{$a}s |" . PHP_EOL, $result['code'], $result['result'], $result['pass'], $result['time'], $result['avg']);
}
echo $line;

?>
PHP 5.1.2

Code: Select all

10,000 interations
+---------------------------------------------------------------------------------------------------+--------+------+------------+------------+
| Code                                                                                              | Result | Pass | Time       | Average    |
+---------------------------------------------------------------------------------------------------+--------+------+------------+------------+
| preg_match('#^[a-z0-9]*$#i','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')           |      1 | PASS | 0.11596100 | 0.00001160 |
| preg_match('#^[a-z0-9]+$#i','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')           |      1 | PASS | 0.11462300 | 0.00001146 |
| preg_match('#^[a-zA-Z0-9]*$#','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')         |      1 | PASS | 0.11865000 | 0.00001187 |
| preg_match('#^[A-z0-9]*$#','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')            |      1 | PASS | 0.11458500 | 0.00001146 |
| eregi('^[a-z0-9]*$','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                   |      1 | PASS | 0.15699600 | 0.00001570 |
| eregi('^[a-z0-9]+$','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                   |      1 | PASS | 0.14902200 | 0.00001490 |
| ereg('^[a-zA-Z0-9]*$','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                 |      1 | PASS | 0.15475600 | 0.00001548 |
| ereg('^[A-z0-9]*$','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                    |      1 | PASS | 0.15353700 | 0.00001535 |
| ctype_alnum('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                           |   true | PASS | 0.09177500 | 0.00000918 |
| preg_match('#^[a-z0-9]*$#i','This is exactly 64 bytes long, not counting the terminating byte')   |      0 | PASS | 0.12162800 | 0.00001216 |
| preg_match('#^[a-z0-9]+$#i','This is exactly 64 bytes long, not counting the terminating byte')   |      0 | PASS | 0.11854600 | 0.00001185 |
| preg_match('#^[a-zA-Z0-9]*$#','This is exactly 64 bytes long, not counting the terminating byte') |      0 | PASS | 0.12182100 | 0.00001218 |
| preg_match('#^[A-z0-9]*$#','This is exactly 64 bytes long, not counting the terminating byte')    |      0 | PASS | 0.12280300 | 0.00001228 |
| eregi('^[a-z0-9]*$','This is exactly 64 bytes long, not counting the terminating byte')           |  false | PASS | 0.16258800 | 0.00001626 |
| eregi('^[a-z0-9]+$','This is exactly 64 bytes long, not counting the terminating byte')           |  false | PASS | 0.14989100 | 0.00001499 |
| ereg('^[a-zA-Z0-9]*$','This is exactly 64 bytes long, not counting the terminating byte')         |  false | PASS | 0.16287400 | 0.00001629 |
| ereg('^[A-z0-9]*$','This is exactly 64 bytes long, not counting the terminating byte')            |  false | PASS | 0.16153800 | 0.00001615 |
| ctype_alnum('This is exactly 64 bytes long, not counting the terminating byte')                   |  false | PASS | 0.08918900 | 0.00000892 |
+---------------------------------------------------------------------------------------------------+--------+------+------------+------------+
PHP 4.4.1

Code: Select all

10,000 interations
+---------------------------------------------------------------------------------------------------+--------+------+------------+------------+
| Code                                                                                              | Result | Pass | Time       | Average    |
+---------------------------------------------------------------------------------------------------+--------+------+------------+------------+
| preg_match('#^[a-z0-9]*$#i','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')           |      1 | PASS | 0.06220700 | 0.00000622 |
| preg_match('#^[a-z0-9]+$#i','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')           |      1 | PASS | 0.06118600 | 0.00000612 |
| preg_match('#^[a-zA-Z0-9]*$#','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')         |      1 | PASS | 0.06278200 | 0.00000628 |
| preg_match('#^[A-z0-9]*$#','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')            |      1 | PASS | 0.06250400 | 0.00000625 |
| eregi('^[a-z0-9]*$','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                   |      1 | PASS | 0.11453300 | 0.00001145 |
| eregi('^[a-z0-9]+$','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                   |      1 | PASS | 0.10529900 | 0.00001053 |
| ereg('^[a-zA-Z0-9]*$','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                 |      1 | PASS | 0.11655600 | 0.00001166 |
| ereg('^[A-z0-9]*$','abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                    |      1 | PASS | 0.11385300 | 0.00001139 |
| ctype_alnum('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq')                           |   true | PASS | 0.05107100 | 0.00000511 |
| preg_match('#^[a-z0-9]*$#i','This is exactly 64 bytes long, not counting the terminating byte')   |      0 | PASS | 1.06066100 | 0.00010607 |
| preg_match('#^[a-z0-9]+$#i','This is exactly 64 bytes long, not counting the terminating byte')   |      0 | PASS | 0.06009300 | 0.00000601 |
| preg_match('#^[a-zA-Z0-9]*$#','This is exactly 64 bytes long, not counting the terminating byte') |      0 | PASS | 0.06421300 | 0.00000642 |
| preg_match('#^[A-z0-9]*$#','This is exactly 64 bytes long, not counting the terminating byte')    |      0 | PASS | 0.06346500 | 0.00000635 |
| eregi('^[a-z0-9]*$','This is exactly 64 bytes long, not counting the terminating byte')           |  false | PASS | 0.12100700 | 0.00001210 |
| eregi('^[a-z0-9]+$','This is exactly 64 bytes long, not counting the terminating byte')           |  false | PASS | 1.10673000 | 0.00011067 |
| ereg('^[a-zA-Z0-9]*$','This is exactly 64 bytes long, not counting the terminating byte')         |  false | PASS | 0.12095500 | 0.00001210 |
| ereg('^[A-z0-9]*$','This is exactly 64 bytes long, not counting the terminating byte')            |  false | PASS | 0.12074500 | 0.00001207 |
| ctype_alnum('This is exactly 64 bytes long, not counting the terminating byte')                   |  false | PASS | 0.04778800 | 0.00000478 |
+---------------------------------------------------------------------------------------------------+--------+------+------------+------------+

Posted: Tue Mar 14, 2006 5:30 am
by keveh
I was just wondering if anybody could help me further, I've tried preg_match instead but I'm getting a strange result.

here is the code:

Code: Select all

$strings = array($firstname, $surname);
$total_error = 0;

 foreach($strings as $value){	        
	if( preg_match("![^-'A-Za-z \t\n]!", $value)){
		// do nothing
	}else{
		$total_error++;
	}
}


if ($total_error > 0){
	do action A
} else {
	do action B
}
So if I type "kevin&" in to $firstname, is should fail verification and do action A, which it does

But if I type "kevin" in to $firstname and "williams" in to $surname, it should pass verification and do action B, but it doesn't, it does action A still.


Can anybody see where I'm going wrong?

Posted: Tue Mar 14, 2006 6:24 am
by matthijs
That's pretty cool Feyd.

Keveh, the preg_match has the ^ before the pattern. Try

Code: Select all

$value = 'kevin&';

if( preg_match("![^-'A-Za-z \t\n]!", $value)){
       echo 'Contains bad characters';
}
The preg_match is doing something like "if the $value contains any characters other then A-Za-z \t\n then return true (echo bad characters in my example)
Try your script like this:

Code: Select all

<?php
$firstname = 'kevin$';
$surname = 'williams';


$strings = array($firstname, $surname);
$total_error = 0;

 foreach($strings as $value){            
    // if $value contains other characters the a-z etc
    if( preg_match("![^-'A-Za-z \t\n]!", $value)){
        $total_error++;   // add an error
    }
}

var_dump ($total_error);

if ($total_error > 0){
    echo '<br>do action A<br>';
} else {
    echo '<br>do action B<br>';
}
?>

Posted: Tue Mar 14, 2006 8:25 am
by keveh
Again, you come through for me matthijs.

Thanks again.