1<?php 2/** 3 * Zend Framework (http://framework.zend.com/) 4 * 5 * @link http://github.com/zendframework/zf2 for the canonical source repository 6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 7 * @license http://framework.zend.com/license/new-bsd New BSD License 8 */ 9 10namespace Zend\Db\Sql\Predicate; 11 12use Countable; 13use Zend\Db\Sql\Exception; 14 15class PredicateSet implements PredicateInterface, Countable 16{ 17 const COMBINED_BY_AND = 'AND'; 18 const OP_AND = 'AND'; 19 20 const COMBINED_BY_OR = 'OR'; 21 const OP_OR = 'OR'; 22 23 protected $defaultCombination = self::COMBINED_BY_AND; 24 protected $predicates = array(); 25 26 /** 27 * Constructor 28 * 29 * @param null|array $predicates 30 * @param string $defaultCombination 31 */ 32 public function __construct(array $predicates = null, $defaultCombination = self::COMBINED_BY_AND) 33 { 34 $this->defaultCombination = $defaultCombination; 35 if ($predicates) { 36 foreach ($predicates as $predicate) { 37 $this->addPredicate($predicate); 38 } 39 } 40 } 41 42 /** 43 * Add predicate to set 44 * 45 * @param PredicateInterface $predicate 46 * @param string $combination 47 * @return PredicateSet 48 */ 49 public function addPredicate(PredicateInterface $predicate, $combination = null) 50 { 51 if ($combination === null || !in_array($combination, array(self::OP_AND, self::OP_OR))) { 52 $combination = $this->defaultCombination; 53 } 54 55 if ($combination == self::OP_OR) { 56 $this->orPredicate($predicate); 57 return $this; 58 } 59 60 $this->andPredicate($predicate); 61 return $this; 62 } 63 64 /** 65 * Add predicates to set 66 * 67 * @param PredicateInterface|\Closure|string|array $predicates 68 * @param string $combination 69 * @return PredicateSet 70 */ 71 public function addPredicates($predicates, $combination = self::OP_AND) 72 { 73 if ($predicates === null) { 74 throw new Exception\InvalidArgumentException('Predicate cannot be null'); 75 } 76 if ($predicates instanceof PredicateInterface) { 77 $this->addPredicate($predicates, $combination); 78 return $this; 79 } 80 if ($predicates instanceof \Closure) { 81 $predicates($this); 82 return $this; 83 } 84 if (is_string($predicates)) { 85 // String $predicate should be passed as an expression 86 $predicates = (strpos($predicates, Expression::PLACEHOLDER) !== false) 87 ? new Expression($predicates) : new Literal($predicates); 88 $this->addPredicate($predicates, $combination); 89 return $this; 90 } 91 if (is_array($predicates)) { 92 foreach ($predicates as $pkey => $pvalue) { 93 // loop through predicates 94 if (is_string($pkey)) { 95 if (strpos($pkey, '?') !== false) { 96 // First, process strings that the abstraction replacement character ? 97 // as an Expression predicate 98 $predicates = new Expression($pkey, $pvalue); 99 } elseif ($pvalue === null) { // Otherwise, if still a string, do something intelligent with the PHP type provided 100 // map PHP null to SQL IS NULL expression 101 $predicates = new IsNull($pkey); 102 } elseif (is_array($pvalue)) { 103 // if the value is an array, assume IN() is desired 104 $predicates = new In($pkey, $pvalue); 105 } elseif ($pvalue instanceof PredicateInterface) { 106 throw new Exception\InvalidArgumentException( 107 'Using Predicate must not use string keys' 108 ); 109 } else { 110 // otherwise assume that array('foo' => 'bar') means "foo" = 'bar' 111 $predicates = new Operator($pkey, Operator::OP_EQ, $pvalue); 112 } 113 } elseif ($pvalue instanceof PredicateInterface) { 114 // Predicate type is ok 115 $predicates = $pvalue; 116 } else { 117 // must be an array of expressions (with int-indexed array) 118 $predicates = (strpos($pvalue, Expression::PLACEHOLDER) !== false) 119 ? new Expression($pvalue) : new Literal($pvalue); 120 } 121 $this->addPredicate($predicates, $combination); 122 } 123 } 124 return $this; 125 } 126 127 /** 128 * Return the predicates 129 * 130 * @return PredicateInterface[] 131 */ 132 public function getPredicates() 133 { 134 return $this->predicates; 135 } 136 137 /** 138 * Add predicate using OR operator 139 * 140 * @param PredicateInterface $predicate 141 * @return PredicateSet 142 */ 143 public function orPredicate(PredicateInterface $predicate) 144 { 145 $this->predicates[] = array(self::OP_OR, $predicate); 146 return $this; 147 } 148 149 /** 150 * Add predicate using AND operator 151 * 152 * @param PredicateInterface $predicate 153 * @return PredicateSet 154 */ 155 public function andPredicate(PredicateInterface $predicate) 156 { 157 $this->predicates[] = array(self::OP_AND, $predicate); 158 return $this; 159 } 160 161 /** 162 * Get predicate parts for where statement 163 * 164 * @return array 165 */ 166 public function getExpressionData() 167 { 168 $parts = array(); 169 for ($i = 0, $count = count($this->predicates); $i < $count; $i++) { 170 /** @var $predicate PredicateInterface */ 171 $predicate = $this->predicates[$i][1]; 172 173 if ($predicate instanceof PredicateSet) { 174 $parts[] = '('; 175 } 176 177 $parts = array_merge($parts, $predicate->getExpressionData()); 178 179 if ($predicate instanceof PredicateSet) { 180 $parts[] = ')'; 181 } 182 183 if (isset($this->predicates[$i+1])) { 184 $parts[] = sprintf(' %s ', $this->predicates[$i+1][0]); 185 } 186 } 187 return $parts; 188 } 189 190 /** 191 * Get count of attached predicates 192 * 193 * @return int 194 */ 195 public function count() 196 { 197 return count($this->predicates); 198 } 199} 200