/**
  * Compares string subclauses in this group for potential simplification
  *
  * Eg: a.b = 1 OR a.b = 2 OR a.b = 3  =>  a.b IN (1,2,3)
  *     a.b != 'a' AND a.b != 'b'  =>  a.b NOT IN ('a','b') 
  */
 public function simplify($opts)
 {
     // For the first item, get the comparison type
     $cType = null;
     foreach ($this->items as $subClause) {
         if (is_string($subClause)) {
             $column = $op = $value = null;
             SubclauseParser::parse($subClause, $column, $op, $value);
             if (!$cType) {
                 $cType = $op;
             } else {
                 if ($cType != $op) {
                     break;
                 }
             }
         }
     }
 }
 /**
  * Recursive function to build the code from nested ClauseGroup objects
  */
 private function buildRecursive(ClauseGroup $group, $level, &$cid, $simplifyOpts = null)
 {
     $lines = array();
     $i = 0;
     foreach ($group->getSubclauses() as $subClause) {
         if ($subClause instanceof ClauseGroup) {
             // Pass the simplification options to the ClauseGroup
             $subClause->simplify($simplifyOpts);
             $block = $this->buildRecursive($subClause, $level + 1, $cid, $simplifyOpts);
             $type = $subClause->getType();
             switch ($type) {
                 case ClauseGroup::BOOLEAN_AND:
                     break;
                 case ClauseGroup::BOOLEAN_OR:
                     break;
                 default:
                     throw new Exception('Unrecognised boolean operator found');
             }
             // Copy code into lines
             foreach ($block as $pieces) {
                 array_push($lines, $pieces);
             }
         } else {
             // Build a criterion statement
             SubclauseParser::parse($subClause, $col, $op, $value);
             // This is the name of the criterion object
             $crit = '$' . $this->cPrefix . $cid;
             // Modify the value if required
             $value = $this->valuePostProcess($op, $value);
             // Loop through the operators we presently know about
             $line = null;
             foreach ($this->ops as $thatOp => $syntax) {
                 if ($op == $thatOp) {
                     $cid++;
                     // For the equality operator...
                     if (is_null($syntax)) {
                         $statement = "{$crit} = \$c->getNewCriterion({$col}, {$value});\n";
                     } elseif (is_null($value)) {
                         $statement = "{$crit} = \$c->getNewCriterion({$col}, null, Criteria::{$syntax});\n";
                     } else {
                         $statement = "{$crit} = \$c->getNewCriterion({$col}, {$value}, Criteria::{$syntax});\n";
                     }
                     $line = $this->makeLine($statement, $level, $i == 0, $crit);
                     array_push($lines, $line);
                     break;
                 }
             }
             // Moan if the operator is not found, as it was detected by SubclauseParser!
             if (is_null($line)) {
                 throw new Exception("Unrecognised comparison operator ('{$op}') found");
             }
         }
         $i++;
     }
     // OK, link up the criterions at our current level
     $crits = '';
     $first = null;
     $code = '';
     foreach ($lines as $line) {
         if ($line['level'] == $level) {
             $crit = $line['crit'];
             if (!$first) {
                 $first = $crit;
             } else {
                 $op = $this->boolOpToPropelOp($group->getType());
                 $code .= "{$first}->{$op}({$crit});\n";
             }
             $crits .= $line['crit'] . ' ';
         }
     }
     // Trim off the last \n we've given the code block
     $code = substr($code, 0, strlen($code) - 1);
     $type = $group->getType();
     $str = "{$code}\n";
     if ($this->comments) {
         $str = "// Perform {$type} at level {$level} ({$crits})\n{$str}";
     }
     $str = "\n{$str}";
     $line = $this->makeLine($str, $level - 1, false, $first);
     array_push($lines, $line);
     // Link into $c if last item
     if ($level == 0) {
         // Determine the right select method and loop to use
         if ($this->returnType == self::RETURN_ARRAY) {
             $method = 'doSelect';
             $loop = "foreach (\$result as \$obj)\n{\n\t//\$val = \$obj->getValue();\n}";
             $postOp = "";
         } else {
             // Determine fetch mode
             $fetchMode = $this->resultSetType;
             if ($fetchMode == self::RESULTSET_NUM) {
                 $col1 = '1';
                 $col2 = '2';
             } else {
                 $col1 = "'col1'";
                 $col2 = "'col2'";
             }
             $method = 'doSelectRS';
             $loop = "while (\$result->next())\n{\n\t//\$str = \$result->getString({$col1});\n\t//\$int = \$result->getInt({$col2});\n}";
             $postOp = "\$result->setFetchMode(ResultSet::{$fetchMode});\n";
         }
         // OK, add the select part...
         $statement = "\n";
         if ($this->comments) {
             $statement .= "// Remember to change the peer class here for the correct one in your model\n";
         }
         $statement .= "\$c->add({$first});\n\$result = TablePeer::{$method}(\$c);\n{$postOp}\n";
         // ... now add a demo loop
         if ($this->loop) {
             if ($this->comments) {
                 $statement .= "// This loop will of course need to be edited to work\n";
             }
             $statement .= $loop;
         }
         $line = $this->makeLine($statement, -1);
         array_push($lines, $line);
     }
     return $lines;
 }
<?php

require_once dirname(__FILE__) . '/../bootstrap/unit.php';
// Symfony classes
$path = realpath(dirname(__FILE__) . '/../../lib');
require_once $path . '/symfony/exception/sfException.class.php';
require_once $path . '/symfony/config/sfConfig.class.php';
require_once $path . '/symfony/util/sfToolkit.class.php';
require_once $path . '/symfony/util/sfInflector.class.php';
$path = dirname(__FILE__) . '/../../plugins/sfPropelQueryBuilderPlugin/modules/sfPropelQueryBuilder/lib';
require_once $path . '/SubclauseParser.class.php';
require_once $path . '/BadSubclause.class.php';
$a = new sfException();
$t = new lime_test(3, new lime_output_color());
$t->diag('SubclauseParser::parse');
// Test numeric equality comparison
$col = $op = $value = null;
SubclauseParser::parse('table.column = 1', $col, $op, $value);
$ok = $col == 'TablePeer::COLUMN' && $op == '=' && $value == '1';
$t->ok($ok, 'decode a column equality comparison');
// Test LIKE
$col = $op = $value = null;
SubclauseParser::parse("table.column LIKE 'A%'", $col, $op, $value);
$ok = $col == 'TablePeer::COLUMN' && $op == 'LIKE' && $value == "'A%'";
$t->ok($ok, 'decode a column string LIKE comparison');
// Test numeric IN
$col = $op = $value = null;
SubclauseParser::parse("table.column IN (1, 2)", $col, $op, $value);
echo "{$col}\n{$op}\n{$value}\n";
$ok = $col == 'TablePeer::COLUMN' && $op == 'IN' && $value == "(1, 2)";
$t->ok($ok, 'decode a column numeric IN test');