Completed
by Woody
03:25
created

### Conditions   A

#### Complexity

 Total Complexity 32

#### Size/Duplication

 Total Lines 242 Duplicated Lines 0 %

#### Coupling/Cohesion

 Components 2 Dependencies 2

#### Test Coverage

 Coverage 100%

#### Importance

 Changes 0
Metric Value
wmc 32
lcom 2
cbo 2
dl 0
loc 242
ccs 76
cts 76
cp 1
rs 9.6
c 0
b 0
f 0

#### 20 Methods

Rating   Name   Duplication   Size   Complexity
A make() 0 8 2
A with() 0 4 1
A andWith() 0 4 1
A orWith() 0 4 1
A group() 0 4 1
A andGroup() 0 4 1
A orGroup() 0 4 1
A end() 0 4 2
A sql() 0 6 1
A params() 0 4 1
A __construct() 0 4 1
 1 `with(\$condition, ...\$params);` 18 ` }` 19 28 ` return \$statment;` 20 ` }` 21 22 ` /**` 23 ` * Alias of andWith().` 24 ` */` 25 27 ` public function with(string \$condition, ...\$params): self` 26 ` {` 27 27 ` return \$this->andWith(\$condition, ...\$params);` 28 ` }` 29 30 ` /**` 31 ` * Add a condition that will be applied with a logical "AND".` 32 ` */` 33 27 ` public function andWith(string \$condition, ...\$params): self` 34 ` {` 35 27 ` return \$this->addCondition('AND', \$condition, \$params);` 36 ` }` 37 38 ` /**` 39 ` * Add a condition that will be applied with a logical "OR".` 40 ` */` 41 4 ` public function orWith(string \$condition, ...\$params): self` 42 ` {` 43 4 ` return \$this->addCondition('OR', \$condition, \$params);` 44 ` }` 45 46 ` /**` 47 ` * Alias for andGroup().` 48 ` */` 49 3 ` public function group(): Conditions` 50 ` {` 51 3 ` return \$this->andGroup();` 52 ` }` 53 54 ` /**` 55 ` * Start a new grouping that will be applied with a logical "AND".` 56 ` *` 57 ` * Exit the group with end().` 58 ` */` 59 3 ` public function andGroup(): Conditions` 60 ` {` 61 3 ` return \$this->addConditionGroup('AND');` 62 ` }` 63 64 ` /**` 65 ` * Start a new grouping that will be applied with a logical "OR".` 66 ` *` 67 ` * Exit the group with end().` 68 ` */` 69 2 ` public function orGroup(): Conditions` 70 ` {` 71 2 ` return \$this->addConditionGroup('OR');` 72 ` }` 73 74 ` /**` 75 ` * Exit the current grouping and return the parent statement.` 76 ` *` 77 ` * If no parent exists, the current conditions will be returned.` 78 ` *` 79 ` * @return Conditions` 80 ` */` 81 4 ` public function end(): Conditions` 82 ` {` 83 4 ` return \$this->parent ?: \$this;` 84 ` }` 85 86 ` // Statement` 87 26 ` public function sql(Identifier \$identifier = null): string` 88 ` {` 89 26 ` \$identifier = \$this->getDefaultIdentifier(\$identifier);` 90 26 ` \$expression = \array_reduce(\$this->parts, \$this->sqlReducer(), '');` 91 26 ` return \$identifier->escapeExpression(\$expression);` 92 ` }` 93 94 ` // Statement` 95 16 ` public function params(): array` 96 ` {` 97 16 ` return \array_reduce(\$this->parts, \$this->paramReducer(), []);` 98 ` }` 99 100 ` /**` 101 ` * @var array` 102 ` */` 103 ` protected \$parts = [];` 104 105 ` /**` 106 ` * @var Conditions` 107 ` */` 108 ` protected \$parent;` 109 110 28 ` protected function __construct(Conditions \$parent = null)` 111 ` {` 112 28 ` \$this->parent = \$parent;` 113 28 ` }` 114 115 ` /**` 116 ` * Add a condition to the current conditions, expanding IN values.` 117 ` */` 118 27 ` protected function addCondition(string \$type, string \$condition, array \$params): self` 119 ` {` 120 27 ` \$this->parts[] = compact('type', 'condition', 'params');` 121 27 ` return \$this;` 122 ` }` 123 124 ` /**` 125 ` * Add a condition group to the current conditions.` 126 ` */` 127 4 ` protected function addConditionGroup(string \$type): Conditions` 128 ` {` 129 4 ` \$condition = new static(\$this);` 130 4 ` \$this->parts[] = compact('type', 'condition');` 131 4 ` return \$condition;` 132 ` }` 133 134 ` /**` 135 ` * Get a function to reduce condition parts to a SQL string.` 136 ` */` 137 26 ` protected function sqlReducer(): callable` 138 ` {` 139 ` return function (string \$sql, array \$part): string {` 140 26 ` if (\$this->isCondition(\$part['condition'])) {` 141 ` // (...)` 142 3 ` \$statement = "({\$part['condition']->sql()})";` 143 ` } else {` 144 ` // foo = ?` 145 26 ` \$statement = \$this->replaceStatementParams(\$part['condition'], \$part['params']);` 146 ` }` 147 26 ` if (\$sql) {` 148 ` // AND ...` 149 6 ` \$statement = "{\$part['type']} \$statement";` 150 ` }` 151 26 ` return \trim(\$sql . ' ' . \$statement);` 152 26 ` };` 153 ` }` 154 155 156 ` /**` 157 ` * Get a function to reduce parameters to a single list.` 158 ` */` 159 16 ` protected function paramReducer(): callable` 160 ` {` 161 ` return function (array \$params, array \$part): array {` 162 16 ` if (\$this->isCondition(\$part['condition'])) {` 163 ` // Conditions have a parameter list already` 164 2 ` return \array_merge(\$params, \$part['condition']->params());` 165 ` }` 166 ` // Otherwise convert the list to a list of lists for flattening` 167 16 ` return \array_merge(\$params, ...\array_map(\$this->paramLister(), \$part['params']));` 168 16 ` };` 169 ` }` 170 171 ` /**` 172 ` * Convert all parameters to an array for flattening.` 173 ` */` 174 16 ` protected function paramLister(): callable` 175 ` {` 176 ` return function (\$param): array {` 177 15 ` if (\$this->isStatement(\$param)) {` 178 ` // Statements have a parameter list already` 179 3 ` return \$param->params();` 180 ` }` 181 ` // Otherwise convert to a list` 182 14 ` return [\$param];` 183 16 ` };` 184 ` }` 185 186 ` /**` 187 ` * Check if a condition is a sub-condition.` 188 ` */` 189 27 ` protected function isCondition(\$condition): bool` 190 ` {` 191 27 ` if (\is_object(\$condition) === false) {` 192 27 ` return false;` 193 ` }` 194 3 ` return \$condition instanceof Conditions;` 195 ` }` 196 197 ` /**` 198 ` * Check if a parameter is a statement.` 199 ` */` 200 15 ` protected function isStatement(\$param): bool` 201 ` {` 202 15 ` if (\is_object(\$param) === false) {` 203 14 ` return false;` 204 ` }` 205 3 ` return \$param instanceof Statement;` 206 ` }` 207 208 ` /**` 209 ` * Check if any parameter is a statement.` 210 ` */` 211 26 ` protected function hasStatementParam(array \$params): bool` 212 ` {` 213 26 ` foreach (\$params as \$param) {` 214 14 ` if (\$this->isStatement(\$param)) {` 215 14 ` return true;` 216 ` }` 217 ` }` 218 24 ` return false;` 219 ` }` 220 221 ` /**` 222 ` * Replacement statement parameters with SQL expression.` 223 ` */` 224 26 ` protected function replaceStatementParams(string \$statement, array \$params): string` 225 ` {` 226 26 ` if (\$this->hasStatementParam(\$params) === false) {` 227 24 ` return \$statement;` 228 ` }` 229 ` // Maintain an offset position, as preg_replace_callback() does not provide one` 230 3 ` \$index = 0;` 231 3 ` return \preg_replace_callback('/\?/', function (\$matches) use (&\$index, \$params) {` 232 ` try {` 233 3 ` if (\$this->isStatement(\$params[\$index])) {` 234 ` // Replace any statement placeholder with the generated SQL` 235 3 ` return \$params[\$index]->sql();` 236 ` } else {` 237 ` // And leave all other placeholders intact` 238 1 ` return \$matches[0];` 239 ` }` 240 ` } finally {` 241 ` // This funky usage of finally allows us to increment the offset` 242 ` // after all other code in the block has been executed.` 243 3 ` \$index++;` 244 ` }` 245 3 ` }, \$statement);` 246 ` }` 247 `}` 248