Need help : building an 'if' condition on the fly.

PHP programming forum. Ask questions or help people concerning PHP code. Don't understand a function? Need help implementing a class? Don't understand a class? Here is where to ask. Remember to do your homework!

Moderator: General Moderators

romain.pelissier
Forum Newbie
Posts: 18
Joined: Fri Jun 15, 2007 12:01 pm

Need help : building an 'if' condition on the fly.

Post by romain.pelissier »

Hi,
This is my very first post here and also my very first php code. I hope that someone could help me.
I would like to create the condition of the 'if' function on the fly, meaning :

Code: Select all

$condition="$a > $b"; // this variable is a string that contain the 'if' condition 
.... 
$a=1; 
$b=1; 
if ($condition) { 
... 
}

I hope you see what I mean here... In fact, I try to build the condition of the 'if' function so at the end it 'virtually' looks like this:

Code: Select all

if ($a > $b){ 
... 
}
Of course, here that does not work because $a=$b.

I have already posted on some french php forum http://www.phpfrance.com/forums/voir_re ... php#198405 (by the way, I french) but I don't have the answer yet. They pointed me with several options but in my case, that does not solve my issue. Why? Simply because It is a little more complex that the example I gave above.
So, let me be a little more specific on my code and what I want :
(please, don't tell me my code is ungly, I already know that : I am not a professional programmer and this is my first attemtp to use php ... :wink: )

Code: Select all

function getFiles($path) { 
   //Function takes a path, and returns a numerically indexed array of associative arrays containing file information, 
   //sorted by the file name (case insensitive).  If two files are identical when compared without case, they will sort 
   //relative to each other in the order presented by readdir() 
   $files = array(); 
   $fileNames = array(); 
   $i = 0; 
    
   if (is_dir($path)) { 
       if ($dh = opendir($path)) { 
           while (($file = readdir($dh)) !== false) { 
               if ($file == "." || $file == "..") continue; 
               $fullpath = $path . "/" . $file; 
               $fkey = strtolower($file); 
               while (array_key_exists($fkey,$fileNames)) $fkey .= " "; 
               $a = stat($fullpath); 
               $files[$fkey]['size'] = $a['size']; 
               if ($a['size'] == 0) $files[$fkey]['sizetext'] = "-"; 
               else if ($a['size'] > 1024) $files[$fkey]['sizetext'] = (ceil($a['size']/1024*100)/100) . " K"; 
               else if ($a['size'] > 1024*1024) $files[$fkey]['sizetext'] = (ceil($a['size']/(1024*1024)*100)/100) . " Mb"; 
               else $files[$fkey]['sizetext'] = $a['size'] . " bytes"; 
               $files[$fkey]['name'] = $file; 
               $files[$fkey]['type'] = filetype($fullpath); 
        $t = extractdateinfo($file); 
        $dat1 = $t[0]; 
        $files[$fkey]['datedebut'] = substr($dat1, 0,strpos($dat1, "-")); 
        $files[$fkey]['datefin'] = substr($dat1, strrpos($dat1, "-") + 1); 
        // insertion de la cle pour la date de modification du fichier 
        $files[$fkey]['datemodif'] = date("Ymd", filemtime($fullpath)); 
               $fileNames[$i++] = $fkey; 
           } 
           closedir($dh); 
       } else die ("Cannot open directory:  $path"); 
   } else die ("Path is not a directory:  $path"); 
   sort($fileNames,SORT_STRING); 
   $sortedFiles = array(); 
   $i = 0; 
   foreach($fileNames as $f) $sortedFiles[$i++] = $files[$f]; 
    
   return $sortedFiles; 
} 

function mainmanage($tabfiles,$criteria1,$rule1) 
        { 
        $patern1='['.$criteria1.']'; 
        $patern2='['.$criteria2.']'; 
        $r=0; 
        $o=0; 
        $tab01=array(); 
        $tab02=array(); 

        foreach ($tabfiles as $fi) 
                { 
                $ext = substr($fi['name'], strrpos($fi['name'], '.') + 1); 
                if ( $ext == "xls" ) 
                        { 
                        if (preg_match($patern1, $fi['name'])) 
                                { 
                                $is_date=date("Ymd");                 
                                $debut = substr($fi['datedebut'],0, 6); 
                                $fin = substr($fi['datefin'], 0, 6); 
                                $dateactu = substr($is_date, 0, 6); 
                                //$dateactu = substr($dd1, 0, 6); 
                                print "</br>\n"; 
                                print "fin = ".$fin; 
                                print "</br>\n"; 
                                print "actu =".$dateactu; 
                                print "</br>\n"; 
                                print "condition = ".$rule1; 
                                print "</br>\n"; 
                             
                                if ($rule1) 
                                    { 
                                    $lm =  date("F d Y H:i:s.", filemtime($fi['name'])); 
                                    $year = substr($fi['datemodif'], 0, 4); 
                                    $month = substr($fi['datemodif'], 4, 2); 
                                    $day = substr($fi['datemodif'], 6, 2); 
                                    $formateddate = date("l, dS F, Y", mktime(0, 0, 0, $month, $day, $year)); 
                                    $d1 = substr($fi['datedebut'],0 , 4) . "-" . substr($fi['datedebut'], 4, 2) . "-" . substr($fi['datedebut'], 6, 2); 
                                    $d2 = substr($fi['datefin'],0 , 4) . "-" . substr($fi['datefin'], 4, 2) . "-" . substr($fi['datefin'], 6, 2); 
                                    $str="&nbsp;&nbsp;&nbsp;&nbsp;<b><a style=\"font-family:verdana\"> Du $d1 au $d2 :  </a><a style=\"font-family:verdana\" href=\"$fi[name]\">[xls]</a></b><br>\n"; 
                                    $v=strval($r); 
                                    $tab01[$v]['datedebut']=$fi['datedebut']; 
                                    $tab01[$v]['datefin']=$fi['datefin']; 
                                    $tab01[$v]['str']=$str; 
                                    $r++; 
                                    } 
                            } 
                    } 
                } 
                return array( $tab01); 
        } 
         
         
$path = $folder_path; 
$files = getFiles($path."/");         
$fresult=mainmanage ($files, "Horaire_Analyste", '($dateactu >= $debut) && ($dateactu < $fin)');
The getFiles function return from a given path an array that contain all files in a folder
The mainmanage function should allow me that from this array to check a couple of things:
- check if the file extension is xls
- check if the current file name (from the array that I am looping in) match a preg_match patern
- check, after a file manipulation where I extract some date infos, if the current date is between an interval of date (from the file name string manipulation). If the test is successfull then the result are inserted in an array

This array will be used later to display the results on the page.

The file format should looks like : yadayada_yyyymmdd-yyyymmdd.xls

As you can see, I have tried to give as a paramter the condition of the 'if' function but in this example that does not work

I would like to use a 'generic' function to handle that. I call this function several time in the page and only few parameters change from one call to the other : the array of file name, the patern for the preg_match and the 'if' condition. Then I use the resulting array to display the result.

I have done several tests ...
If I send as a parameter for the 'if' function condition '($dateactu >= $debut) && ($dateactu < $fin)' ou '('.$dateactu.' >= '.$debut.') && ('.$dateactu.' < '.$fin.')' the result within the function is alway the same : ( >= ) && ( < ). If I put the try this condition inside the function (as a test string) as this : '('.$dateactu.' >= '.$debut.') && ('.$dateactu.' < '.$fin.')' then in this case the variables are correctly interpreted and the result is (200706 >= 200706) && (200706 < 200709).

So it seems that the variables in my condition parameter are eveluated to NULL et never been updated even after $debut, $fin and $dateactu are set correcty in the function...

I don't know if this is even possible, I only try to find the correct answer to this issue. Maybe they are some ways to solve this problem in other way, in this case, juste let me know what do you think that could solve my problem. Sorry again for my english ...
smudge
Forum Contributor
Posts: 151
Joined: Sun May 20, 2007 12:13 pm

Post by smudge »

you might want to examine the eval() function
edit:also, in the first block of code ($condition="$a < $b") you do not need the quotes if $a and $b are ready to test, since comparisons are operations like adding and subtracting, except that it returns true or false. If statements test whether the expression in the () is true, if it is, do the if block, if not move on. So, if you had:

Code: Select all

$a=1;
$b=1;
$condition=$a<$b;
if($condition){
  echo "Hmm... 1 is less than 1";
}
//which could be rewritten to
$a=1;
$b=1;
if($a<$b){
  ...
}
Last edited by smudge on Fri Jun 15, 2007 12:42 pm, edited 2 times in total.
romain.pelissier
Forum Newbie
Posts: 18
Joined: Fri Jun 15, 2007 12:01 pm

Post by romain.pelissier »

Thanks for the reply.

A lot of php users prevent from using th eval() function. Some says because of security issues (don't know which). I have also posted this question here http://www.phpfreaks.com/forums/index.p ... 271.0.html and a guy gives me a nice trick using functions. I will not close this topic as resolved until I have several options or point of vue.

Thanks again!
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

The way you were doing it, with passing the rule, why not pass the params and the condition and see how that evaluates?
romain.pelissier
Forum Newbie
Posts: 18
Joined: Fri Jun 15, 2007 12:01 pm

Post by romain.pelissier »

Thanks for the reply.

I am sorry but I don't really understand what you mean by
why not pass the params and the condition
... can you explain a little what your are thinking about?
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Code: Select all

<?php
function mainmanage($tabfiles,$criteria1,$cond1, $cond2, $eval = '>') {
  // the rest of that mess of code 
  $rule = false;
  switch ($eval) {
    case '>':
      $rule = $cond1 > $cond2;
      break;
    case '>=':
      $rule = $cond1 >= $cond2;
      break;
    case '<':
      $rule = $cond1 < $cond2;
      break;
    case '<=':
      $rule = $cond1 <= $cond2;
      break;
  }

  if ($rule) {
    // do what to do if true
  } else { 
    // DO what to do if false
  }
}
?>
romain.pelissier
Forum Newbie
Posts: 18
Joined: Fri Jun 15, 2007 12:01 pm

Post by romain.pelissier »

smudge wrote:you might want to examine the eval() function
edit:also, in the first block of code ($condition="$a < $b") you do not need the quotes if $a and $b are ready to test, since comparisons are operations like adding and subtracting, except that it returns true or false. If statements test whether the expression in the () is true, if it is, do the if block, if not move on. So, if you had:

Code: Select all

$a=1;
$b=1;
$condition=$a<$b;
if($condition){
  echo "Hmm... 1 is less than 1";
}
//which could be rewritten to
$a=1;
$b=1;
if($a<$b){
  ...
}
I am pretty sure that it will work but my problem is that I will set the value for $a and $b after I have set the condition, like this :

$condition=$a > $b;
test_function($condition);
...
function test_function($rule){
$a=1;
$b=2;
if ($rule){
...}
}

As you can see the rule is created before the variable are set. I will test but I am afraid to have the result ' > ' instead of '1>2'
romain.pelissier
Forum Newbie
Posts: 18
Joined: Fri Jun 15, 2007 12:01 pm

Post by romain.pelissier »

Everah wrote:

Code: Select all

<?php
function mainmanage($tabfiles,$criteria1,$cond1, $cond2, $eval = '>') {
  // the rest of that mess of code 
  $rule = false;
  switch ($eval) {
    case '>':
      $rule = $cond1 > $cond2;
      break;
    case '>=':
      $rule = $cond1 >= $cond2;
      break;
    case '<':
      $rule = $cond1 < $cond2;
      break;
    case '<=':
      $rule = $cond1 <= $cond2;
      break;
  }

  if ($rule) {
    // do what to do if true
  } else { 
    // DO what to do if false
  }
}
?>
Nice, but my condition could be $a>$b && $b>$c;
I have posted the same question here http://www.phpfreaks.com/forums/index.p ... 271.0.html and a guy have given a way to use complex condition syntax handling (for the && at least).

I will play with this.
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

For what you want to do, you will have a lot of comparisons to make. Basically you need to cover every possible combination of an infinite number of conditional checks.

Maybe it is time to rethink your use of this procedure and move on to something a little easier to understand/develop?
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

well, you could do that using variable variables plus a little bit of OOP magic

Code: Select all

class Condition {
  private $_operator;
  private $_operand1Name;
  private $_operand2Name;
  private $_operand1;
  private $_operand2;
  public function __construct($operand1Name, $operator, $operand2Name) {
    $this->_operator = $operator;
    $this->_operand1Name = $operand1Name;
    $this->_operand2Name = $operand2Name;
  }
  public function evaluate() {
    switch ($this->_operator) {
       case '<': return $this->_operand1 < $this->_operand2;
       case '>': return $this->_operand1 > $this->_operand2;
       case '==': return $this->_operand1 == $this->_operand2;
       case '>=': return $this->_operand1 >= $this->_operand2;
       case '<=': return $this->_operand1 <= $this->_operand2;
       default: trigger_error('Unknown operand', E_USER_WARNING); return false;
    }
  }
  public function setOperand1($value) { $this->_operand1 = $value;}
  public function setOperand2($value) { $this->_operand2 = $value;}
  public function getOperand1Name() { return $this->_operand1Name;}
  public function getOperand2Name() { return $this->_operand2Name;}
}

$condition = new Condition('a', '<', 'b');
test_function($condition);
//...
function test_function($rule){
   $a=1;
   $b=2;
   $rule->setOperand1(${$rule->getOperand1Name()});
   $rule->setOperand2(${$rule->getOperand2Name()});
   if ($rule->evaluate()){
    var_dump(true);
   // ...
   }
}
romain.pelissier
Forum Newbie
Posts: 18
Joined: Fri Jun 15, 2007 12:01 pm

Post by romain.pelissier »

Wow ! I am impress! I will test this but can it work with && conditions like $a>$b && $b>$c ?
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

romain.pelissier wrote:Wow ! I am impress! I will test this but can it work with && conditions like $a>$b && $b>$c ?
No, not yet.
This one, however, can:

Code: Select all

class Condition {
  private $_operator;
  private $_operand1;
  private $_operand2;
  private $_operand1Value = null;
  private $_operand2Value = null;
  private $_context;

  public function __construct($operand1, $operator, $operand2) {
    $this->_operator = $operator;
    $this->_operand1 = $operand1;
    $this->_operand2 = $operand2;
  }

  public function __toString() {
      $op1 = $this->_operand1 instanceof Condition ? $this->_operand1->__toString() : $this->_operand1;
      $op2 = $this->_operand2 instanceof Condition ? $this->_operand2->__toString() : $this->_operand2;
      return '( ' . $op1 . ' ' . $this->_operator . ' ' . $op2 . ' )';
  }

  private function _resolveOperands($context = false) {
    if (!is_null($this->_operand1Value) || !is_null($this->_operand2Value)) {
      return;
    }

    if (!$context) {
      $context = $this->_context;
    }

    if ($this->_operand1 instanceof Condition) {
      $this->_operand1->_resolveOperands($context);
      $this->_operand1Value = $this->_operand1->evaluate();
    } else {
      $this->_operand1Value = $context[$this->_operand1];
    }

    if ($this->_operand2 instanceof Condition) {
      $this->_operand2->_resolveOperands($context);
      $this->_operand2Value = $this->_operand2->evaluate();
    } else {
      $this->_operand2Value = $context[$this->_operand2];
    }
  }

  public function setContext($context) {
    $this->_context = $context;
  }

  public function evaluate() {
    $this->_resolveOperands();
    switch ($this->_operator) {
      case '<': return $this->_operand1Value < $this->_operand2Value;
      case '>': return $this->_operand1Value > $this->_operand2Value;
      case '==': return $this->_operand1Value == $this->_operand2Value;
      case '>=': return $this->_operand1Value >= $this->_operand2Value;
      case '<=': return $this->_operand1Value <= $this->_operand2Value;
      case '!=': return $this->_operand1Value != $this->_operand2Value;
      case 'and': return $this->_operand1Value && $this->_operand2Value;
      case 'or': return $this->_operand1Value || $this->_operand2Value;
      default: trigger_error('Unknown operator', E_USER_WARNING); return false;
    }
  }
}

$condition = new Condition(new Condition('a', '<', 'b'), 'or', new Condition('a', '==', 'b'));
test_function($condition);
//...
function test_function($rule){
   $a=2;
   $b=2;
   // no more hacks
   $rule->setContext(get_defined_vars());
   if ($rule->evaluate()){
    var_dump(true);
   // ...
   }
}
Cleaned up context passing
romain.pelissier
Forum Newbie
Posts: 18
Joined: Fri Jun 15, 2007 12:01 pm

Post by romain.pelissier »

Thank you very much Weirdan, can I ask you one little more explanation about your code?
What if if you have in the condition a > b and b>c (meaning 3 variables to in 2 conditions).

I guess it is :

Code: Select all

$condition = new Condition(new Condition('a', '<', 'b'), 'or', new Condition('b', '==', 'c')); 
test_function($condition); 
//... 
function test_function($rule){ 
   $a=2; 
   $b=2;
   $c=2;
   // no more hacks 
   $rule->setContext(get_defined_vars()); 
   if ($rule->evaluate()){ 
    var_dump(true); 
   // ... 
   } 
}
just to be sure I am correct for the

Code: Select all

$rule->setContext(get_defined_vars());
part.

Another little one : does this code works with PHP 4.3.9? It seems not because I have an error :

Code: Select all

PHP Parse error:  parse error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in /var/www/html/test.php on line 3
Content-type: text/html
X-Powered-By: PHP/4.3.9

Thanks
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Post by RobertGonzalez »

Weirdan's code is PHP5.
User avatar
Weirdan
Moderator
Posts: 5978
Joined: Mon Nov 03, 2003 6:13 pm
Location: Odessa, Ukraine

Post by Weirdan »

What if if you have in the condition a > b and b>c (meaning 3 variables to in 2 conditions).
You're almost right. This line should be changed though:

Code: Select all

// from
$condition = new Condition(new Condition('a', '<', 'b'), 'or', new Condition('b', '==', 'c'));
// to
$condition = new Condition(new Condition('a', '>', 'b'), 'and', new Condition('b', '>', 'c'));
The code is for PHP5, but converting it to PHP4 is quite straightforward:
  • remove all 'public'/'private'/'protected' specifiers (for class variables: replace them with 'var' keyword)
  • change all '$var instanceof Class' to 'is_a($var, "Class")'
  • change constructor name from '__construct' to 'Condition'
Post Reply