PHP validation question - php

How do I check and see if a user enters only numbers and is at least 4 numbers long using PHP?

Mark Byers' suggestion is good, but here's another way:
$valid = ctype_digit($number) && strlen($number) >= 4;

You could use a regular expression:
/^\d{4,}$/
Example usage:
$s = "7325";
if (preg_match('/^\d{4,}$/', $s)) {
echo "matches";
}

ctype_digit() && strlen() wins
<?php
function benchmark($callback){
echo sprintf('%-30s: ', $callback);
$t = microtime(true);
foreach(range(1, 10000) as $n){
call_user_func($callback);
}
echo (microtime(true)-$t)."\n";
}
function mark_byers_preg_match(){
$s = "7325";
preg_match('/^\d{4,}$/', $s);
}
function notjim_ctype_digit_strlen(){
$number = 7325;
ctype_digit($number) && strlen($number) >= 4;
}
function tomalak_intval_broken(){
$check = 7325;
intval($check) == $check && $check >= 1000 && $check <= 9999;
}
benchmark('mark_byers_preg_match');
benchmark('notjim_ctype_digit_strlen');
benchmark('tomalak_intval_broken');
?>
results
mark_byers_preg_match : 0.029040098190308
notjim_ctype_digit_strlen : 0.026585817337036
tomalak_intval_broken : 0.019872903823853
Note: #Tomalak's does not work with numbers starting with 0 so it does not qualify
Edit: #kiethjgrant's solution was removed because intval(0000) evaluates as false when it should be true.

Do you have any example code to start with?
To strictly answer your question, you could use a regex like if(preg_match('/^\d{4,}$/', $input)....
But there's a lot more to consider here: you need to consider both validation and filtering (and you're best to keep the two separate issues). If you're strictly checking for an integer, then I suppose you're safe from SQL injection, XSS, etc., but you really need to have a handle on those issues, because sooner or later you're going to need to filter & validate something other than a simple integer.

you should always use the most efficient way to do it
if ( is_numeric($imput) && isset($input[3]) )
{
// your code
}
isset() is a language construct, which is always faster than strlen().
isset($input[n-1]) tells you whether string(data which passes through form is always string) has at least n long.
is_numeric() checks it is a valid num string.
i think it is better than ctype_digit() && strlen().

Related

I want the minimum to be 0 but the substraction is still negative

I'm beggining in OOP with PHP, and i want to make 2 opponent fight, with the function fight. I want to have a $degat variable which is a result of a radom number between 1 to strength of attacker - (minus) dexterity of defender.
So the result must be of minimum 0.
I have this code and when I var_dump it I still have negative value. I can't see what i'm doing wrong.
public function fight(Fighter $target)
{
// $degat = (rand(1, $this->strength) - $target->dexterity) >= 0 ? (rand(1, $this->strength) - $target->dexterity) : 0;
if ((rand(1, $this->strength) - $target->dexterity) > 0) {
$degat = (rand(1, $this->strength) - $target->dexterity);
} else {
$degat = 0;
};
var_dump($degat);
$target->life -= $degat;
}
thanks !
Well, the problem here is the fact that each time you call rand, the result is different. So, you should save the expression with rand and then reuse it.
Also, the whole thing could be done slightly shorter with the use of ternary operator.
$expr = (rand(1, $this->strength) - $target->dexterity);
$degat = $expr > 0 ? $expr : 0
Some notes:
rand does not generate cryptographically secure values and could also depend on the staring seed, you could also consider using random_int, I think it helps randomizing your game, for sure
read more about ternary operator here: https://www.php.net/manual/en/language.operators.comparison.php
Instead of storing the result of rand(1, $this->strength) - $target->dexterity) in a variable, you could also omit the if-statement completely and reduce your method to:
public function fight(Fighter $target)
{
// max(a,b) returns the higher number from a and b
$target->life -= max(0, rand(1, $this->strength) - $target->dexterity);
}

Form input in php being integer

I currently have a form containing an input:
<input type="text" name="score" id="score" value="" />
Criteria: I need the value in this input to be below 40 and a positive integer or zero.
Having read up on php.net about is_int() and is_numeric(). It advises using is_numeric() with form fields as these are always numeric strings.
I want to check if the value meets the above criteria but don't follow how I would do this in the above situation.
<?php
$score = $_POST['score'];
if(is_numeric($score) && $score <= 40){
// Do good stuff
} else {
// Don't do good stuff
} ?>
My issue with the above is that floats would pass this test and without using something like (int) $score I can't use is_int() which then negates the is_numeric check.
Am I missing something here?
Use ctype_digit() to make sure the string consists only of numbers, and therefore is an integer - technically, this returns true also with very large numbers that are beyond int's scope. Note that this method will not recognize negative numbers.
<?php
$score = $_POST['score'];
if(ctype_digit($score) && $score <= 40){
// Do good stuff
} else {
// Don't do good stuff
}
?>

Password Validation With Multiple Rules

I'm attempting to write a regex in PHP that validates the following:
At least 10 chars
Has at least 2 Upper-case characters
Has at least 2 Numbers OR Symbols
I've looked at just about every reference I can find but, to no avail.
I guess I can test individually, but that makes me very sad :(
Can someone please help? (And send me to a spot where I can learn in plain English Reg Ex?)
This picture is worth more than 1000 words
(and that's a lot of entropy)
(image via XKCD)
With this in mind you might want to consider dropping rules 2 & 3 if password length is higher than X (say.. 20) or increase the minimum to at least 16 characters (as the only rule).
As for your requirement:
As opposed to having one big, ugly, hard-to-maintain, advanced RegExp you might want to break the problem in smaller parts and tackle each bit separately using dedicated functions.
For this you could look at ctype_* functions, count_chars() and MultiByte String Functions.
Now the ugly:
This advanced RegEx will return true or false according to your rules:
preg_match('/^(?=.{10,}$)(?=.*?[A-Z].*?[A-Z])(?=.*?([\x20-\x40\x5b-\x60\x7b-\x7e\x80-\xbf]).*?(?1).*?$).*$/',$string);
Test demo here: http://regex101.com/r/qE9eB2
1st part (LookAhead) : (?=.{10,}$) will check string length and continue if it has at least 10 characters. You could drop this and do a check with strlen() or even better mb_strlen().
2nd part (also a LookAhead): (?=.*?[A-Z].*?[A-Z]) will check for the presence of 2 UPPERCASE characters. You could also do a $upper=preg_replace('/[^A-Z]/','',$string) instead and count the chars in $upper to be more than two.
3rd LookAhead uses a character class: [\x20-\x40\x5b-\x60\x7b-\x7e\x80-\xbf] with hex escaped character ranges for common symbols (pretty much all the symbols one could find on an average keyboard). You could also do a $sym=preg_replace('/[^a-zA-Z]/','',$string) instead and count the chars in $sym to be more than two. Note: to make it shorter I used a recursive group (?1) to not repeat the same character class again
For learning, the most comprehensive RegExp reference I know of is: regular-expressions.info
You can use lookaheads to make sure that what you are looking for is contained appropriately.
/(?=.*[A-Z].*[A-Z])(?=.*[^a-zA-Z].*[^a-zA-Z]).{10,}/
I have always preferred good old procedural code for handling stuff like this. Regular expressions can be useful but they can also be a little cumbersome, especially for code maintenance and quick scanning (regular expressions are not exactly examples of readability).
function strContains($string, $contains, $n = 1, $exact = false) {
$length = strlen($string);
$tally = 0;
for ($i = 0; $i < $length; $i++) {
if (strpos($contains, $string[$i]) !== false) {
$tally++;
}
}
return ($exact ? $tally == $n : $tally >= $n);
}
function validPassword($password) {
if (strlen($password) < 10) {
return false;
}
$upperChars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$upperCount = 2;
if (strContains($password, $upperChars, $upperCount) === false) {
return false;
}
$numSymChars = '0123456789!"#$%&\'()*+,-./:;<=>?#[\\]^_`{|}~';
$numSymCount = 2;
if (strContains($password, $numSymChars, $numSymCount) === false) {
return false;
}
return true;
}

Simple? Logic flaw with my function

I have a problem, it'll probably become apparent as you read my function, but I can't figure out what to do.
The issue is, I need to use "do, while", because I need the result of "do", to test in "while". The problem is, when the while gets all 4 conditions returning false, it quits, but that leaves me with a bad code.
I need to regenerate a code until it contains no indistinguishable characters.
function make_code(){
do{
$prefix = mt_rand(0, mt_getrandmax());
$code = uniqid($prefix);//good to make sure we always have a unique string to work with, even with no seed supplied.
$sha1 = sha1($code);
$base_convert = base_convert($sha1, 16, 36);//expand hex with the rest of the alphabet.
$substr = substr($base_convert, 0, 12);//we only want the first 12 characters.
$strtoupper = strtoupper($substr);//for aesthetics.
$str_split = str_split($strtoupper, 4);//seperate into chunks.
$voucher_code = $str_split[0] . self::CS . $str_split[1] . self::CS . $str_split[2];//build
}
while(
(stristr($voucher_code, "o") === false)
&& (stristr($voucher_code, "0") === false)
&& (stristr($voucher_code, "1") === false)
&& (stristr($voucher_code, "i") === false));
return $voucher_code;
}
}
Thanks for any help offered.
Wouldn't it be easier to just present this code in a font that DOES make those characters distinguishable? That being said, simply use a regex to "simplify" your multiple string matches:
do {
...
while (preg_match('/[01lo]/i', $voucher_code));
Eliminating those characters from usage just makes it that much more likely you'll end up with a duplicate voucher.

PHP Number Cleaner RegEx

I have a function I use in PHP to work with numbers. The intent is to clean the number and, optionally, convert nulls to zero. It began for me for use in prep for sql, but is now used in more places. Here it is:
function clean_num ($num, $null_to_zero = true) {
$num = preg_replace("/[^-0-9.0-9$]/","",$num);
if (strlen($num) == 0)
$num = ($null_to_zero) ? 0 : null;
else if (strlen($num) == 1 && ($num == '-' || $num == '.'))
$num = ($null_to_zero) ? 0 : null;
return $num;
}
Does anyone have any ideas on a faster, better way of doing this? It works, the regex is simple enough and should cover all cases I need, but... A diff regex might do all the same without other junk. Regex is not my strength. Thanks!
The regex [^-0-9.0-9$] matches any char that is
not a hyphen
not a digit
not a .
not a $
there is no need to have two 0-9 in the char class, so effectively your regex is: [^-0-9.$] or [^-\d.$]

Categories