Page 2 of 2

Posted: Tue Jun 19, 2007 8:47 am
by romain.pelissier
Weirdan, my php einstein, thanks a lot again.

Still, few problems. I have made the modifications to make your code works in php 4 but I then come with some issue.

First, it seems that the function is_a() is not know on my server ... I have find on the internet this fix to make the function works. I have put the following code :

Code: Select all

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

  function is_a($anObject, $aClass) {
   return get_class($anObject) == strtolower($aClass)
     or is_subclass_of($anObject, $aClass);
// ...
  }
So the is_a() function works now. But PHP still complaining with this error message :

Code: Select all

PHP Warning:  Illegal offset type
It seems that it does not like the following :

Code: Select all

$this->_operand2Value = $context[$this->_operand2];
I have tried this fix found on the internet http://wordpress.org/support/rss/topic/119656 but it does not seem to work for me or maybe I don't know how to set it correctly ...

Can you help me with this?

Posted: Tue Jun 19, 2007 9:23 am
by superdezign
Try typecasting $this->_operand2 to an integer as the index of $context. I'm not sure what it does, but it's telling you that it's either not a string or not an integer.

Code: Select all

$this->_operand2Value = $context[(int)$this->_operand2];
See if it works.

Posted: Tue Jun 19, 2007 9:46 am
by romain.pelissier
Again you have the right solution! Now PHP only displays 'Notice'.
But ...
It seems that the condition are not interpreted correcly ... I have the rules :

Code: Select all

$condition = new Condition(new Condition('a', '<', 'b'), 'or', new Condition('a', '==', 'b'));
and

Code: Select all

function test_function($rule)
{
   $a=1;
   $b=2;
   // no more hacks
   $rule->setContext(get_defined_vars());
   if ($rule->evaluate())
   {
    var_dump(true);
    print "ok";
   // ...
   }
  else {
   print "no ok";
  }
}
it display 'no ok' so even if the value of $a and $b are correct, the condition should result to a 'true' which does not seems to be the case here ...
Any idea?

Posted: Tue Jun 19, 2007 10:26 am
by superdezign
'a' and 'b' are strings. I think you're after $a and $b. (Maybe)

Edit: Or not. I haven't read the class yet.

Posted: Tue Jun 19, 2007 10:31 am
by romain.pelissier
If I change

Code: Select all

$condition = new Condition(new Condition('a', '<', 'b'), 'or', new Condition('a', '==', 'b'));
to

Code: Select all

$condition = new Condition(new Condition('$a', '<', '$b'), 'or', new Condition('$a', '==', '$b'));
I have got the same result ...

Posted: Tue Jun 19, 2007 4:05 pm
by feyd
Likely, you need to lose the single quotes around $a and $b.

Posted: Tue Jun 19, 2007 6:16 pm
by Weirdan
superdezign wrote: I think you're after $a and $b. (Maybe)
No.
feyd wrote: Likely, you need to lose the single quotes around $a and $b.
No.
superdezign wrote:

Code: Select all

$this->_operand2Value = $context[(int)$this->_operand2];
That's wrong. $this->_operand1 and $this->_operand2 supposed to be strings because they hold variable names (or subconditions, but that's handled in another branches of those if statements). Constructor accepts variable names without $ signs because that's how they are returned by get_defined_vars() function.

romain.pelissier, please post your code (Condition class) for us to see if you have converted it properly.

Posted: Tue Jun 19, 2007 6:27 pm
by superdezign
Yeah, that's why I shut up. I read the class and it does stuff I've never tried before. :P

Posted: Wed Jun 20, 2007 8:07 am
by romain.pelissier
Hi Wierdan, master of the php universe,

Here is the code that I have modified to run in PHP4 including the is_a() function. I have commented the modification that I have made.

Code: Select all

<?
class Condition { 
  // Removed private and replace with var
  var $_operator;
  var $_operand1;
  var $_operand2;
  var $_operand1Value = null;
  var $_operand2Value = null;
  var $_context;

  // The function is_a() was added because without it, PHP gives me some errors,
  // This piece of code was found on the internet and on the php.net web site.
  function is_a($anObject, $aClass) {
   return get_class($anObject) == strtolower($aClass)
     or is_subclass_of($anObject, $aClass);
  }

  function Condition($operand1, $operator, $operand2) //rename from _construct to Condition
  {
    $this->_operator = $operator;
    $this->_operand1 = $operand1;
    $this->_operand2 = $operand2;
  }

  function __toString()
  {
      // change $var instanceof by the function is_a($var, "Class")
      $op1 = $this->is_a(_operand1, "Condition") ? $this->_operand1->__toString() : $this->_operand1;
      $op2 = $this->is_a(_operand2, "Condition") ? $this->_operand2->__toString() : $this->_operand2;
      return '( ' . $op1 . ' ' . $this->_operator . ' ' . $op2 . ' )';
  }

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

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

    // change $var instanceof by the function is_a($var, "Class")
    if ($this->is_a(_operand1, "Condition"))
    {
      $this->_operand1->_resolveOperands($context);
      $this->_operand1Value = $this->_operand1->evaluate();
    }
    else
    {
      //change from $context[(int)$this->_operand1]; to $context[(int)$this->_operand1
      $this->_operand1Value = $context[(int)$this->_operand1]; 
    }

    // change $var instanceof by the function is_a($var, "Class")
    if ($this->is_a(_operand2, "Condition"))
    {
      $this->_operand2->_resolveOperands($context);
      $this->_operand2Value = $this->_operand2->evaluate();
    }
    else
    {
      //change from $context[(int)$this->_operand1]; to $context[(int)$this->_operand1
      $this->_operand2Value = $context[(int)$this->_operand2];
    }
  }

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

  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=1;
   $b=2;
   // no more hacks
   $rule->setContext(get_defined_vars());
   if ($rule->evaluate())
   {
    var_dump(true);
    print "ok";
   // ...
   }
  else {
   print "no ok";
  }
}

?>
Thanks again weirdan!

Posted: Wed Jun 20, 2007 1:52 pm
by Weirdan
Fixes are trivial:
  • $this->is_a(_operand1, "Condition"). This is wrong. It should be $this->is_a($this->_operand1, "Condition"). Same goes for _operand2 of course.
  • new Condition('$a', '<', '$b'). This is wrong as well. Dollar signs shouldn't be used here, so it should be new Condition('a', '<', 'b')
  • $this->_operand1Value = $context[(int)$this->_operand1]; and this is terribly wrong. It should be $this->_operand1Value = $context[$this->_operand1];

Posted: Wed Jun 20, 2007 3:45 pm
by romain.pelissier
Wierdan, the php wikipedia man,

Again, your magic has resolve all my issues. What can I say expect that you should be a god in php programming.

Thanks again. I will post tomorrow the code for PHP5 and PHP4 so other users could see and use it.

Tomorrow, I will set this topic as resolve since, I think, the code and methodology provided by weirdan match exactly what I wanted to acheive. But if other users still want to contribute, they are more than welcome.

Thanks to all people that have contributed to help me.

Posted: Fri Jun 22, 2007 3:20 pm
by romain.pelissier
Wierdan, I still have a little question for you if you still watching this topic.

A strange thing happen. I need to do additional tests but I found a possible issue.

Your code works wonderfully but it seems that in certain circumstancies, the condition does not work.

Let me explain. So the code below works :

Code: Select all

$condition = new Condition('a', '>=', 'b');
test_function($condition);
//...
function test_function($rule)
{
   $is_date=date("Ymd");
   //$debut = intval(substr($fi['datedebut'],0, 6));
   //$fin = intval(substr($fi['datefin'], 0, 6));
   $dateactu = intval(substr($is_date, 0, 6));

   $a=1;
   $b=2;
   // no more hacks
   $rule->setContext(get_defined_vars());
   if ($rule->evaluate())
   {
    var_dump(true);
    print "ok";
   // ...
   }
  else {
   print "no ok";
  }
}
In my case however, I do additional operation in the function, like a foreach loop that retreive the value of a and b from an array. It seems that when you initialise a and b in the same function several time and call the $rule->evaluate() function, the code seems to not be able to see that the variables value change.

If the condition is :

Code: Select all

a >=b
and I have an array of value for a and b like this :

Code: Select all

array[0][start]=1
array[0][end]=0
array[1][start]=2
array[1][end]=5
As you can see, at position [0], condition a > b is true if a=array[0][start] and b=array[0][end] and the condition should be false for [1]

In the code, I dynamically change the value of a and b from my array:

Code: Select all

function test_function($rule)
{
   $is_date=date("Ymd");
   //$debut = intval(substr($fi['datedebut'],0, 6));
   //$fin = intval(substr($fi['datefin'], 0, 6));
   $dateactu = intval(substr($is_date, 0, 6));
   foreach ($array as $val){
     $a=$val['start'];
     $b=$val['end'];
     // no more hacks
     $rule->setContext(get_defined_vars());
     if ($rule->evaluate())
     {
      var_dump(true);
      print "ok";
     // ...
     }
    else {
     print "no ok";
    }
  }
}
(Please note that I have not tested yet the code above but I have the quite same structure in another code and this does not work for me, it is just to give you an idea. I will test THIS code as soon as I can to be sure.)

So, first 'ok' should be displayed and then 'no ok'. But for me, I still have some 'ok' ...
I am not really sure but it seems that PHP does not seems to see that the value for a and b have changed ...

An idea ?

Posted: Sat Jun 23, 2007 4:29 am
by Weirdan
it could be because of this safeguard:

Code: Select all

if (!is_null($this->_operand1Value) || !is_null($this->_operand2Value))
    {
      return;
    }
During subsequent invocations operand values are already resolved, so it's not picking up new ones.

Posted: Sun Jun 24, 2007 1:29 pm
by Weirdan
ok, fixed those problems:

Code: Select all

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

  function is_a($anObject, $aClass) {
    return
      is_object($anObject)
      and (
          strtolower(get_class($anObject)) == strtolower($aClass)
          or is_subclass_of($anObject, $aClass)
      );
  }

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

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

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

    if ($this->is_a($this->_operand2, "Condition")) {
      $this->_operand2Value = $this->_operand2->evaluate($context);
    } else {
      $this->_operand2Value = $context[$this->_operand2];
    }
  }

  function evaluate($context)
  {
    $this->_resolveOperands($context);
    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'));
echo $condition->__toString() . "\n";
test_function($condition);
//...
function test_function($rule)
{
   $array = array(
     array('a' => 1, 'b' => 2),
     array('a' => 2, 'b' => 1),
   );

   foreach ($array as $element) {
     $a = $element['a'];
     $b = $element['b'];
     if ($rule->evaluate(get_defined_vars())) {
       var_dump(true);
     } else {
       var_dump(false);
     }
   }
}

Posted: Tue Jun 26, 2007 1:38 pm
by romain.pelissier
Weirdan, you should have some php code in your genes :D

It works this time.

I will post soon the code for PHP5 and PHP4 for those who are interested.

Thanks again.