Using PHP eval() in if statement - php

Not sure how to use the eval() I thought I read the documentation correctly and am trying to do this (I know the logic is awful, but I was just trying it out).
if($showUnlisted){
$unlistedIfStatement = '!$price || $car["sale_price"] == 0 ||($priceFrom <= $car["sale_price"] && $priceTo >= $car["sale_price"])';
}
else{
$unlistedIfStatement = '!$price ||($priceFrom <= $car["sale_price"] && $priceTo >= $car["sale_price"])';
}
if(eval($unlistedIfStatement)){
//DO SOME STUFF
}
I get this error:
Parse error: syntax error, unexpected end of file
Looking at these examples(http://us1.php.net/eval) it should run whatever is in the string as a line of code. Also this definition "The eval() function evaluates a string as PHP code." from http://www.w3schools.com/php/func_misc_eval.asp Basically, I'm trying to store a line of code in a variable and have it run like I would do in JavaScript. Very new to PHP, I'm sorry if my question is dumb.
EDIT:
I've read the many posts as to why the eval() is a bad tool, I honestly am just asking out of curiosity.
EDIT2:
meagar's code worked splendidly, the only thing I had to do to get it to work was add semi-colons before the string ended. I went to edit his post, but my edit was denied. Here is the code that worked for me....Obviously I'll be changing it to something that my peers will not poke fun at.
if($showUnlisted){
$unlistedIfStatement = 'return !$price || $car["sale_price"] == 0 ||($priceFrom <= $car["sale_price"] && $priceTo >= $car["sale_price"]);';
}
else{
$unlistedIfStatement = 'return !$price ||($priceFrom <= $car["sale_price"] && $priceTo >= $car["sale_price"]);';
}
if(eval($unlistedIfStatement)){
//DO SOME STUFF
}

The documentation that you linked to is very clear:
eval() returns NULL unless return is called in the evaluated code, in which case the value passed to return is returned.
Each of your eval'd lines must return a value, or they evaluate to null:
if($showUnlisted){
$unlistedIfStatement = 'return !$price || $car["sale_price"] == 0 ||($priceFrom <= $car["sale_price"] && $priceTo >= $car["sale_price"])';
}
else{
$unlistedIfStatement = 'return !$price ||($priceFrom <= $car["sale_price"] && $priceTo >= $car["sale_price"])';
}
if(eval($unlistedIfStatement)){
//DO SOME STUFF
}

As indicated in another answer, eval() returns null in absence of a return value. However, what you're actually looking for is a callback:
Somewhere in your code:
function showUnlisted() {
return !$price || $car["sale_price"] == 0 || ($priceFrom <= $car["sale_price"] && $priceTo >= $car["sale_price"]);
}
function dontShowUnlisted() {
return !$price ||($priceFrom <= $car["sale_price"] && $priceTo >= $car["sale_price"]);
}
Then, where you need to decide between these functions:
if ($showUnlisted) {
$appropriateFunction = 'showUnlisted';
} else {
$appropriateFunction = 'dontShowUnlisted';
}
if (call_user_func($appropriateFunction)) {
//do stuff
}
This prevents you from falling prey to the evils of eval, lets you test those functions, utilize IDEs more effectively, and predict the outcome better. Passing functions as objects is a useful thing, and while awkward in PHP, a common practice in more modern languages (Scala, C#, etc.).

Related

Is this a good way of writing long conditions in PHP?

I have to evaluate a very long condition in PHP, so, to avoid errors and trying to write more readable code, I did the following:
//this returns 1 when true, and nothing when false, although expected TRUE or FALSE
$isNameValid=strlen($dataDecoded['nombre'])>=3;
$isDescriptionValid=(strlen($dataDecoded['descripcion'])>=10) && strlen($dataDecoded['descripcion'])<=300;
$isPriceValid=$dataDecoded['precio'] >0;
$isImageValid=(($dataDecoded['imagen'] != "") && ($dataDecoded['imagen'] != NULL) );
And now, I can make the following:
if($isNameValid==1 && $isDescriptionValid==1 && $isPriceValid==1 && $isImageValid==1)
{
echo "ok";
}
else{
echo "no";
}
It seems to work fine, but maybe is a weird way of doing things. I wanted to avoid the following, which I find more confusing and easy to make a mistake
if(strlen($dataDecoded['nombre'])>=3 && ... && ...)
Is there a better way to do that? Is wrong what I did? Thanks
I don't care for creating extra variables here; this makes code difficult to maintain and unreusable. I'd recommend breaking your validation logic into easy-to-read, maintainable, reusable functions:
function valid($data) {
return validName($data['nombre']) &&
validDescription($data['descripcion']) &&
validPrice($data['precio']) &&
validImage($data['imagen']);
}
function validName($name) {
return strlen($name) >= 3;
}
function validDescription($desc) {
return strlen($desc) >= 10 && strlen($desc) <= 300;
}
function validPrice($price) {
return $price > 0;
}
function validImage($image) {
return $image !== "" && $image != NULL;
}
$dataDecoded = [
"nombre" => "foo",
"descripcion" => "foo bar foo bar",
"precio" => 15,
"imagen" => "foo.png"
];
// now your main code is beautiful:
echo (valid($dataDecoded) ? "ok" : "no") . "\n";
Yes, that is acceptable. However, your variables there are all boolean, so you don't even need the ==1.
if($isNameValid && $isDescriptionValid && $isPriceValid && $isImageValid)
It really depends on how you want to handle it.
Is switch an option or a viable one?
Is ternary if prettier or handy?
From what I see, I'm guessing you have a validation purpose and a operating incoming depending on the validation. Why not create a function or a class that handles your input and validates? And in there, you can have all the dirty code you'd want. On your logical code, you'd just have to do (e.g of a class)
$someClass = new SomeClass();
$someClass->validate($fields);
if ($someClass->isValidated()) ...
This way, you'd actually follow some standards whereas the purpose of it would be to work as a validator for (all of? depends on your needs) your data
E.g of ternary ifs
$isNameValid = count($dataDecoded['nombre'])>=3 ? true : false;
$isDescriptionValid = count($dataDecoded['descripcion']) >= 10 && count($dataDecoded['descripcion']) <= 300 ? true : false;
$isPriceValid = count($dataDecoded['precio']) > 0 ? true : false;
$isImageValid = empty($dataDecoded['imagen']) === false ? true : false;
if ($isNameValid && $isDescriptionValid && $isPriceValid && $isImageValid) ...

Boolean handling in PHP

I have following code in my php script:
if ( $x != 'some_val_1' && $y['index']->title != "some_val_2" && $y['index']->title != "some_val_3" ) {
// Do something.
}
In the code shown above, index may/may not be set and thus when it is not set this code throws a php notice.
I changed it to:
if (isset($y['index']->title)) {
if ( $x != 'some_val_1' && $y['index']->title != "some_val_2" && $y['index']->title != "some_val_3" ) {
// Do something.
}
}
However since the "Do something" part is set to work when isset($y['index']->title is not equal to some_val_2 and some_val_3, the above does not work because the isset() condition I added skips the code.
So now the script does not throw notice but at the cost of completely changing the condition to something not desirable.
How can I change this code to not throw the PHP notice?
You can handle it in different ways, I initialize the variables if they are not defined:
if (! isset($y['index']->title)) {
$y['index']->title = '';
}
if ( $x != 'some_val_1' && $y['index']->title != "some_val_2" && $y['index']->title != "some_val_3" ) {
// Do something.
}
I prefer this way because I don't need to change my code and add if conditions (with isset for example) in all my program. I just add the conditions at the top of my code.
Since you are dealing with an array of objects, you can use the function array_key_exists().
Your code will look like this:
if ( array_key_exists('index',$y) && $x != 'some_val_1' && $y['index']->title != 'some_val_2' && $y['index']->title != 'some_val_3' ) {
// Do something.
}
You can read more about it here: http://php.net/manual/en/function.array-key-exists.php
You can try using isset & in_array in this scenario.
if ( $x != 'some_val_1'
&& isset($y['index']->title)
&& !in_array($y['index']->title, array("some_val_2", "some_val_3") ) ) {
// Do something.
}
The benefit of using in_array is that you validate case-sensitivity by passing TRUE / FALSE in the third parameter.

Inverting Logical AND Condition

I'm working on the following code:
// $data has only one dimension AND at least one of its values start with a "#"
if ( (count($data) == count($data, COUNT_RECURSIVE))
&& (count(preg_grep('~^#~', $data)) > 0) )
{
// do nothing
}
else
{
// do something
}
The logic of this condition is working just fine, however, I would prefer if I could get rid of the empty if block while evaluating the second condition only if the first one yields true - otherwise the preg_grep() call will throw the following notice when $data has more than one dimension:
Notice: Array to string conversion
I know I could use the error suppression operator or some other hacky approaches, but I have the feeling that I'm missing something trivial. Can someone help me out, please?
First, obvious way:
if (!( (count($data) == count($data, COUNT_RECURSIVE))
&& (count(preg_grep('~^#~', $data)) > 0)) )
{
// everything is cool, nothing to do
}
else
{
// do something
}
Second, correct way
if ((count($data) < count($data, COUNT_RECURSIVE))
|| (count(preg_grep('~^#~', $data)) == 0))
{
// everything is cool, nothing to do
}
else
{
// do something
}
if (!((count($data) == count($data, COUNT_RECURSIVE)) && (count(preg_grep('~^#~', $data)) > 0)))
{
// do something
}
if (! (
count($data) == count($data, COUNT_RECURSIVE) &&
count(preg_grep('~^#~', $data)) > 0
)
Wrap the entire group of conditions and stick ! at the start. Line breaks are for readability. I removed the unnecessary () around the individual conditions as well.
I'm not entirely clear what you're asking, but I think you want:
// $data has only one dimension AND at least one of its values start with a "#"
if (!((count($data) == count($data, COUNT_RECURSIVE))
&& (count(preg_grep('~^#~', $data)) > 0)))
{
// do something
}
With this, the block executes if ((count($data) == count($data, COUNT_RECURSIVE)) && (count(preg_grep('~^#~', $data)) > 0)) is false.

Recursive Descent Parser for EBNF in PHP

I am attempting to write a recursive descent parser in PHP for the following EBNF:
EXP ::= < TERM > { ( + | - ) < TERM > }
TERM ::= < FACTOR > { ( * | / ) < FACTOR > }
FACTOR ::= ( < EXP > ) | < DIGIT >
DIGIT ::= 0 | 1 | 2 | 3
I followed this guide which I saw recommended on a similar question. (I searched before I posted)
For the most part, I get how it works and I understand the grammar. I think the problem is within my syntax. I am new to PHP, so I have been referencing W3Schools. I currently am getting the following error with my code:
Warning: Wrong parameter count for exp() .... on line 101
I have tried to look up this error and have not had much luck. I read some posts about people passing in the wrong parameter typed, but I do not have any parameters set for that function. Is there something about PHP I am missing here?
Below is my code, I think the logic is correct since I based it off of the parse tree for the grammar. The $input will be coming from a form box on an HTML page. I also picked up the str_split function from a different post when I discovered that PHP4 does not have it built in.
<html>
<body>
<?php
if(!function_exists("exp")){
function exp(){
term();
while($token == "+" | $token == "-"){
if($token == "+"){
match("+");
term();
}
if($token == "-"){
match("-");
term();
}
}
}//end exp
}
if(!function_exists("term")){
function term(){
factor();
while($token == "*" | $token == "/"){
if($token == "*"){
match("*");
factor();
}
if($token == "/"){
match("/");
factor();
}
}
}//end term
}
if(!function_exists("factor")){
function factor(){
if($token == "("){
match("(");
exp();
if($token == ")")
match(")");
}
else if($token == 0|1|2|3){
if($token == 0)
match(0);
if($token == 1)
match(1);
if($token == 2)
match(2);
if($token == 3)
match(3);
}
else
error();
}//end factor
}
if(!function_exists("match")){
function match($expected){
if($token == $expected)
nextToken();
else
error();
}//end match
}
if(!function_exists("next_Token")){
function nextToken(){
$next++;
$token = $tokenStr[$next];
if($token == "$");
legal();
}
}
if(!function_exists("error")){
function error(){
echo "Illegal token stream, try again";
}
}
if(!function_exists("legal")){
function legal(){
echo "Legal token stream, congrats!";
}
}
if(!function_exists('str_split')) {
function str_split($string, $split_length = 1) {
$array = explode("\r\n", chunk_split($string, $split_length));
array_pop($array);
return $array;
}
}
$tokenStr = str_split($input);
$next = 0;
$token = $tokenStr[0];
exp();
?>
</body>
</html>
So basically I want to know what causes that error and why and am I on the right track in terms of creating this parser.
I appreciate any comments, suggestions, criticisms, water baloons, and tomatoes. Thank you for taking the time to read my post. Have a great day/night.
exp() is a builtin PHP function. You cannot define it under that name.
You should have no reason to use the if(!function_exists(' idiom in normal PHP applications. (It's often used more as a workaround when include scripts clash or identical functions are declared at different places.)
Another syntax problem that I noticed is your use of the bitwise OR. The logical OR should be || or just or.
while($token == "*" | $token == "/"){
I'll turn my wild guess into an answer. So maybe this is, where the problem lies?
http://php.net/manual/en/function.exp.php
There is also a function named exp() in PHP already. You may prefix your function names somehow, or it's better to use classes to avoid name collisions.

If statement structure in PHP

I keep getting an error with the following bit of code. It is probably some small thing but I don't see what is wrong.
while($row = mysql_fetch_array($result))
{
$varp = $row['ustk_retail'];
if ($varp<80000) { $o1 = 1; }
if (($varp=>80000) && ($varp<100000)) { $o2 = "1"; }
if (($varp=>100000) && ($varp<120000)) { $o3 = "1"; }
if (($varp=>120000) && ($varp<140000)) { $o4 = "1"; }
if (($varp=>140000) && ($varp<160000)) { $o5 = "1"; }
if (($varp=>160000) && ($varp<180000)) { $o6 = "1"; }
if (($varp=>180000) && ($varp<200000)) { $o7 = "1"; }
if (($varp=>200000) && ($varp<220000)) { $o8 = "1"; }
if (($varp=>220000) && ($varp<240000)) { $o9 = "1"; }
if (($varp=>240000) && ($varp<260000)) { $o10 = "1"; }
if (($varp=>260000) && ($varp<280000)) { $o11 = "1"; }
if (($varp=>280000) && ($varp<300000)) { $o12 = "1"; }
if ($varp>=300000) { $o13 = "1"; }
}
Running php -l (lint) on your code I get a
Parse error: syntax error, unexpected T_DOUBLE_ARROW
The T_DOUBLE_ARROW token is what PHP expects when assigning array values to array keys.
When comparing for Greater than or equal to the PHP Parser expects T_IS_GREATER_OR_EQUAL, meaning you have to use >= instead of =>.
See
the chapter on Comparison Operators in the PHP Manual and
the List of Parser Tokens in the PHP Manual
Greater than or equal to is >= sign, not =>
Update:
You are right. It's small but hard to find mistake.
It took me to split whole line into pieces to see where the problem is:
<?php
if
(
$varp
=>
80000
)
So, it says parse error on line 5 and I had to doublecheck this operator.
Of course, at first I separated the problem line from the rest of the code to be certain.
You have an expression error.
$varp=>220000 // is not a valid php expression
=> operator is used to assign values in arrays like:
$x = array( 'foo' => 'bar');
>= is the comparation assigment greater than or equal
You have made a mistake in the if conditions. The greater than Equal to sign is >= and not =>.
The answer has already been given but thought this was neat enough to share:
PHP accepts boolean expressions in it's switch statement.
switch(TRUE) {
case $range <= 10: echo "range below or equal to 10"; break;
case $range <= 20: echo "range above 10 below or equal to 20"; break;
case $range <= 30: echo "range above 20 below or equal to 30"; break;
default: echo "high range";
}
In my opinion this generates the cleanest most readable code.
This is more readable and compact way to do the same:
$ranges = range(300000, 80000, -20000);
$index = 1;
$varp = 220001;
foreach ($ranges as $i => $range) {
if ($varp >= $range) {
$index = 13 - $i;
break;
}
}
${'o' . $index} = 1;
Anyway - I think you're doing something wrong with using variable name of result.
You probably want to change ($varp=300000) to ($varp==300000) and it might help to enclose the full if-statement inside (), like this
if($varp80000 && $varp100000 && $varp120000 && $varp140000 && $varp160000 && $varp180000 && $varp200000 && $varp220000 && $varp240000 && $varp260000 && $varp280000 && $varp==300000) { $o13 = "1"; }
On another note, where to these strange $varp#### variables come from?
Not sure whether the code you've posted has gotten messed up somehow, but it looks like you're missing "==" in some of the if conditions. Also, as Skilldrick pointed out, the whole if condition should be in parentheses
"Greater than or equal to is >= NOT =>. You use => for arrays for keys/values.
Add one more bracket around the conditions in if....
if ( ($varp80000) && ($varp100000) && ($varp120000) && ($varp140000) && ($varp160000) && ($varp180000) && ($varp200000) && ($varp220000) && ($varp240000) && ($varp260000) && ($varp280000) && ($varp=300000) ) { $o13 = "1"; }

Categories