private function equalRecurse(ClauseGroup $g1, ClauseGroup $g2, $level)
 {
     if ($g1->getType() != $g2->getType() || $g1->getCount() != $g2->getCount()) {
         return false;
     }
     $subClauses1 = $g1->getSubclauses();
     $subClauses2 = $g2->getSubclauses();
     $ok = true;
     for ($i = 0; $i < count($subClauses1); $i++) {
         $item1 = $subClauses1[$i];
         $item2 = $subClauses2[$i];
         if ($item1 instanceof ClauseGroup) {
             $ok = $this->equalRecurse($item1, $item2, $level + 1);
         } else {
             $ok = $item1 == $item2;
         }
         if (!$ok) {
             break;
         }
     }
     return $ok;
 }
 /**
  * Merge two ClauseGroups
  *
  * NB: first param must be a ClauseGroup (ie not a null)
  * Second param can be either a ClauseGroup, a string clause, or null
  */
 private function groupMerge(ClauseGroup $g1, $g2)
 {
     if (is_null($g2)) {
         return $g1;
     } elseif (is_string($g2)) {
         $clone = clone $g1;
         $clone->addSubclause($g2);
         return $clone;
     } else {
         // If one of the boolean types is null, it can be treated as empty
         $t1 = $g1->getType();
         $t2 = $g2->getType();
         if (is_null($t1)) {
             $t1 = $t2;
         } elseif (is_null($t2)) {
             $t2 = $t1;
         }
         // Can't merge two groups of differing bool operators
         if ($t1 != $t2) {
             throw new Exception("Can't merge two groups of differing boolean operators ({$t1}, {$t2})");
         } else {
             $clone = clone $g1;
             $subClauses = $g2->getSubclauses();
             foreach ($subClauses as $subClause) {
                 //QuotedStringParser::insertStrings($subClause, $this->strings);
                 $clone->addSubclause($subClause);
             }
             // We've made the two types the same, so use one on the clone
             $clone->setType($t1);
             return $clone;
         }
     }
 }
$g1->setType(ClauseGroup::BOOLEAN_AND);
$t->ok($result->equal($g1), 'decode a valid bracketed string containing ANDs and ORs');
$clause1 = "person.age IN (17,18,19,20)";
$clause2 = "person.height IN (100, 101)";
$result = $ca->analyse($clause1);
$rClause1 = $result->getFirstSubclause();
$g = new ClauseGroup();
$g->addSubclause($rClause1);
$t->ok($result->equal($g), 'decode a simple IN statement');
try {
    $clause = $clause1 . ' OR ' . $clause2;
    $result = $ca->analyse($clause);
    $clauses = $result->getSubclauses();
    $g->addSubclause($clauses[1]);
    $g->setType(ClauseGroup::BOOLEAN_OR);
    $ok = $result->equal($g);
} catch (Exception $e) {
    $ok = false;
}
$t->ok($ok, 'decode a two IN statements');
$clause = "person.name IN ('person.age IN (1, 2, 3)')";
try {
    $result = $ca->analyse($clause);
    $rClause = $result->getFirstSubclause();
    $g = new ClauseGroup();
    $g->addSubclause($rClause);
    $ok = $result->equal($g);
} catch (Exception $e) {
    $ok = false;
}
$t->ok($ok, 'decode a simple IN statement containing a misleading IN string');
 /**
  * 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;
 }