Using Eval to perform if comparison of strings in PHP - php

I have some code that looks like this.
eval('$ruleTrue = '."{$value} {$operator} {$value2};");
I am pulling mostly numeric values from a database and comparing them with other numeric values. The operator comes from a database as well. Possible operators are <,>,==.
Well when comparing ints and floats this works perfectly. BUT when comparing strings it breaks. For instance..
WORKS:
5 > 4
$ruleTrue = true
Doesn't Work Right:
John-Adams == Alice
$ruleTrue = true <--- WHY? Because John is not == to Alice.
For some reason my $ruleTrue variable is being returned as true when comparing strings.

You're trying to evaluate this code:
$ruleTrue = John == Alice;
John and Alice aren't strings, they're undefined constants. You want to put quotes around them. But be careful, because if your users are able to edit those fields in your database, they could find a way of unquoting the strings and executing their own php code, which could be disastrous. Eval is very unsecure that way, and you probably shouldn't be using it.

The expression John-Adams == Alice parses somewhat like (John - Adams) == Alice. The left side is trying to subtract two strings (undefined constants are considered 'barewords', and are equal to a stringification of their names; John === 'John', for example), and in order to make sense of such an odd operation, PHP turns both strings into numbers. As integers, both strings have a value of 0, so the left side equates to 0.
An int.
Now, when PHP wants to compare with ==, it wants to coerce both sides into the same type. In this case, it's converting to int. Alice also converts to 0. Both sides being 0, they're "obviously" equal.
In order to prevent this from happening, you should probably put quotes around your values. You might also consider using the strict equals operator (===), unless you really want that type coercion magic.
Alternatively, if you have a known set of operators, you can eliminate eval and make this safer and more robust all around, by making a comparison function that has sub-functions for the operators. Like so:
function compare($value1, $op, $value2) {
static $known_ops = array(
'==' => function($a, $b) { return $a == $b; },
'!=' => function($a, $b) { return $a != $b; },
...
# you can even make up your own operators. For example, Perl's 'eq':
'eq' => function($a, $b) { return "$a" === "$b"; }
...
);
$func = $known_ops[$op];
return $func($value1, $value2);
}
...
$ruleTrue = compare($value, $operator, $value2);
Now you don't have to worry about your values. You do have to worry about $operator, but that's only an issue if you let a user input it without you validating it. In which case, you may want to throw an exception or something if $op wasn't in $known_ops, cause if you leave PHP to handle it, you'll likely get a fatal error when it tries to call null.

Make sure, if the value are strings, they are surrounded with "quotes"

Related

Is there a compelling reason to use PHP's operator === in comparison operations, over ==?

Say I have this code:
$str = '5';
$int = 5;
For comparison, is there any reason to use something like this (with conversion):
if ($int === intval($str)) //...
or do I just use native PHP facilities?
if ($int == $str) //...
To me, == looks simpler, perhaps at the expense of having PHP do the extra work for me.
Using '==' tends to lead to subtle bugs - eg if two strings look like numbers, PHP does not compare them as strings, which can give unexpected results - the most common/scary example is:
<?php
$actual_password = '240610708';
$provided_password = 'QNKCDZO';
// These would presumably be stored in your database
$stored_password_md5 = md5($actual_password); //0e462097431906509019562988736854;
$stored_password_hash = password_hash($actual_password, PASSWORD_DEFAULT);
$computed_password_md5 = md5($provided_password); //0e830400451993494058024219903391
var_dump($stored_password_md5 == $computed_password_md5); // bool(true) - BAD! NO!
var_dump($stored_password_md5 === $computed_password_md5); // bool(false) - Better, but still no. Vulnerable to timing attacks
var_dump(hash_equals($stored_password_md5, $computed_password_md5)); // bool(false) getting somewhere
var_dump(password_verify($provided_password, $stored_password_hash)); // bool(false) best
While in your specific example, this problem doesn't occur, the possible problems lead to a lot of people recommending to /always/ use ===, so you don't have to remember when == is safe and when it isn't.
Depends on what you are trying to do. Some functions might return false or 0 or a positive integer, like strpos(). 0 means the string was found at position 0 so == false would not work as === false.
In your scenario it is fine to use == as this is common when getting values from a DB or $_POST and $_GET, they will always be strings.
Thanks to the comment from Fred Emmott: Be careful, the following return true:
var_dump('0xa' == '10'); // 0xa hex 10 in decimal
var_dump('10pigs' == 10); // pigs truncated
See String conversion to numbers
The == operator just checks to see if the left and right values are equal. But, the === operator (note the extra “=”) actually checks to see if the left and right values are equal, and also checks to see if they are of the same variable type (like whether they are both booleans, ints, etc.).

Finding common characters in strings

I am tring this method to find the common characters in two strings namely, $a and $r, but the first character isn't getting printed . Moreover the $already collects the common characters and prevents them from being printed for multiple times( I need each character to be printed once only) but it isn't doing so. Please tell me what errors I am making.
<?php
$a="BNJUBCI CBDIDIBO";
$r="SBKJOJLBOU";
$already="";
for($i=0;$i<strlen($r);$i++)
{
if (stripos($a,$r[$i])!=FALSE)
{
if (stripos($already,$r[$i])==FALSE)
{
$already=$already.$r[$i];
echo "already=".$already."<br>";
echo $r[$i]."<br>";
}
}
}
?>
Use !==FALSE instead of !=FALSE. The problem is that stripos returns 0 if the needle is at the start of the haystack, and 0 is falsy. By using !== you are forcing it to ensure the result is actually false, and not just 0.
This is actually listed in the docs. An "RTM" might be appropriate here.
Warning
This function may return Boolean FALSE, but may also return a non-Boolean value which evaluates to FALSE. Please read the section on Booleans for more information. Use the === operator for testing the return value of this function.
The simplest way to find the intersection of the two strings in PHP is to turn them into arrays and use the built-in functions for that purpose.
The following will show all the unique and common characters between the two strings.
<?php
$a="BNJUBCI CBDIDIBO";
$r="SBKJOJLBOU";
$a_arr = str_split($a);
$r_arr = str_split($r);
$common = implode(array_unique(array_intersect($a_arr, $r_arr)));
echo "'$common'";
?>
I would think a much simpler solution to this would be to make the strings into arrays and compare those no?
Something like:
<?php
$a="BNJUBCI CBDIDIBO";
$r="SBKJOJLBOU";
$shared = implode( '' , array_intersect( str_split($a) , str_split($r) ) );
?>
That should return you a string of all the characters in $a that are present in $r

Why is my inequality test failing?

The ever flaky Xdebug is on the fritz at the mo' (normal service will be resumed as soon as possible), so I am reduced to "debug by echo".
echo($path_info['filename'] . ' ' . $licence['issue_timestamp'].'<br>');
if ($path_info['filename'] != $licence['issue_timestamp'])
{
die('They are NOT equal');
$_SESSION['error_messages'][] = 'This licence file has been copied';
return False;
}
else
die('They are equal');
outputs
1319266557_ 1319266557
They are equal
Any idea what I am doing wrong? Is there something special about (trailing) underscores?
The docs state:
$a != $b Not equal TRUE if $a is not equal to $b after type juggling.
And that "type juggling" looks mighty suspicious. Elsewhere on that page, it mentions:
If you compare a number with a string or the comparison involves numerical strings, then each string is converted to a number and the comparison performed numerically. These rules also apply to the switch statement. The type conversion does not take place when the comparison is === or !== as this involves comparing the type as well as the value.
And the example is a dead giveaway as to what's happening in your case:
var_dump(100 == "1e2"); // 100 == 100 -> true
In terms of how strings are converted to integers, that can be seen here. The salient bit is (my bold):
If the string does not contain any of the characters '.', 'e', or 'E' and the numeric value fits into integer type limits (as defined by PHP_INT_MAX), the string will be evaluated as an integer. In all other cases it will be evaluated as a float.
The value is given by the initial portion of the string.
Example: $foo = 1 + "10 Small Pigs"; // $foo is integer (11)
That's why "123_" is equal to "123" (a). Bottom line, use !== since that ensures both the value and the type are the same.
(a) See the online PHP executor:
One of the bone-headed things that PHP does is that == only compares values "after type juggling". What this means is that what most sane people think of as == is really === in PHP. Try
$path_info['filename'] !== $licence['issue_timestamp'].

PHP if condition with strings

I am trying to write would be a simple if condition.
function genderMatch($consumerid1, $consumerid2)
{
$gender1=getGender($consumerid1);
$gender2=getGender($consumerid2);
echo $gender1;
echo $gender2;
if($gender1=$gender2)
echo 1;
return 1;
else
echo 0;
return 0;
}
The output of the getGender function is either a M or F. However, no matter what I do gender1 and gender2 are returned as the same. For example I get this output: MF1
I am currently at a loss, any suggestions?
if ($gender1 = $gender2)
assigns the value of $gender2 to $gender1 and proceeds if the result (i.e. the value of $gender2) evaluates to true (every non-empty string does). You want
if ($gender1 == $gender2)
By the way, the whole function could be written shorter, like this:
function genderMatch($cid1, $cid2) {
return getGender($cid1) == getGender($cid2);
}
You have to put two == for comparison. With only one, as you have right now, you are assigning the value to the first variable.
if($gender1=$gender2)
would become
if($gender1==$gender2)
this:
if($gender1=$gender2)
should be
if($gender1==$gender2)
notice the extra ='s sign. I think you might also need curly brackets for multiple lines of an if/else statement.
Your using the assignment operator = instead of comparsion operators == (equal) or === (identical).
Have a look at PHP operators.
You have some structural problems with your code as well as an assignment instead of a comparison.
Your code should look like this:
function genderMatch($consumerid1, $consumerid2){
$gender1=getGender($consumerid1);
$gender2=getGender($consumerid2);
echo $gender1;
echo $gender2;
if($gender1==$gender2){
echo 1;
return 1;
}else{
echo 0;
return 0;
}
}
Notice the double '=' signs in the if statement. This is a comparison. A single '=' is an assignment. Also, if you want to execute more than 1 line of code with an if/else, you need brackets.
You are using a single = which sets the variable, ie. the value of $gender1 is set to be the value of $gender2.
Use the === operator instead: if($gender1 === $gender2). It is usually a good idea to do strict comparisons rather than loose comparisons.
Read more about operators here: php.net
Another alternative is to use strcmp($gender1, $gender2) == 0. Using a comparer method/function is more common in languages where the string-datatype isn´t treated as a primary data-type, eg. C, Java, C#.

What's better, isset or not?

Is there any speed difference between
if (isset($_POST['var']))
or
if ($_POST['var'])
And which is better or are they the same?
It is a good practice to use isset for the following reasons:
If $_POST['var'] is an empty string or "0", isset will still detect that the variable exists.
Not using isset will generate a notice.
They aren't the same. Consider a notional array:
$arr = array(
'a' => false,
'b' => 0,
'c' => '',
'd' => array(),
'e' => null,
'f' => 0.0,
);
Assuming $x is one of those keys ('a' to 'f') and the key 'g' which isn't there it works like this:
$arr[$x] is false for all keys a to g;
isset($arr[$x]) is true for keys a, b, c, d and f but false for e and g; and
array_key_exists($x, $arr) is true for all keys a to f, false for g.
I suggest you look at PHP's type juggling, specifically conversion to booleans.
Lastly, what you're doing is called micro-optimization. Never choose which one of those by whichever is perceived to be faster. Whichever is faster is so negligible in difference that it should never be a factor even if you could reliably determine which is faster (which I'm not sure you could to any statistically significant level).
isset tests that the variable has any value, while the if tests the value of the variable.
For example:
// $_POST['var'] == 'false' (the string false)
if (isset($_POST['var'])) {
// Will enter this if
}
if ($_POST['var']) {
// Won't enter this one
}
The big problem is that the equivalency of the two expressions depends on the value of the variable you are checking, so you can't make assumptions.
In strict PHP, you need to check if a variable is set before using it.
error_reporting(E_ALL | E_STRICT);
What you are doing here
if($var)
Isn't checking if the value is set. So Strict PHP will generate a notice for unset variables. (this happens a lot with arrays)
Also in strict PHP (just an FYI for you or others), using an unset var as an argument in a function will throw a notice and you can't check isset() within the function to avoid that.
Just repeating what others said, if you execute:
if($variable)
and $variable is not set, you'll get a notice error. Plus..
$var = 0;
if($variable) {
//This code will never run, because $var is false
}
but using isset would return true in this case.

Categories