InConditionBuilder::build()   F
last analyzed

Complexity

Conditions 24
Paths 3897

Size

Total Lines 73
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 26
CRAP Score 63.4376

Importance

Changes 0
Metric Value
cc 24
eloc 44
nc 3897
nop 2
dl 0
loc 73
ccs 26
cts 44
cp 0.5909
crap 63.4376
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @link https://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license https://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db\conditions;
9
10
use yii\db\Expression;
11
use yii\db\ExpressionBuilderInterface;
12
use yii\db\ExpressionBuilderTrait;
13
use yii\db\ExpressionInterface;
14
use yii\db\Query;
15
16
/**
17
 * Class InConditionBuilder builds objects of [[InCondition]]
18
 *
19
 * @author Dmytro Naumenko <[email protected]>
20
 * @since 2.0.14
21
 */
22
class InConditionBuilder implements ExpressionBuilderInterface
23
{
24
    use ExpressionBuilderTrait;
25
26
27
    /**
28
     * Method builds the raw SQL from the $expression that will not be additionally
29
     * escaped or quoted.
30
     *
31
     * @param ExpressionInterface|InCondition $expression the expression to be built.
32
     * @param array $params the binding parameters.
33
     * @return string the raw SQL that will not be additionally escaped or quoted.
34
     */
35 1
    public function build(ExpressionInterface $expression, array &$params = [])
36
    {
37 1
        $operator = strtoupper($expression->getOperator());
0 ignored issues
show
Bug introduced by
The method getOperator() does not exist on yii\db\ExpressionInterface. It seems like you code against a sub-type of said class. However, the method does not exist in yii\db\JsonExpression or yii\db\ArrayExpression or yii\db\PdoValue or yii\db\conditions\ConditionInterface or yii\db\conditions\HashCondition or yii\db\conditions\NotCondition. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

37
        $operator = strtoupper($expression->/** @scrutinizer ignore-call */ getOperator());
Loading history...
38 1
        $column = $expression->getColumn();
0 ignored issues
show
Bug introduced by
The method getColumn() does not exist on yii\db\ExpressionInterface. It seems like you code against a sub-type of yii\db\ExpressionInterface such as yii\db\Query or yii\db\Expression or yii\db\conditions\InCondition or yii\db\conditions\BetweenCondition or yii\db\conditions\SimpleCondition. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

38
        /** @scrutinizer ignore-call */ 
39
        $column = $expression->getColumn();
Loading history...
39 1
        $values = $expression->getValues();
0 ignored issues
show
Bug introduced by
The method getValues() does not exist on yii\db\ExpressionInterface. It seems like you code against a sub-type of yii\db\ExpressionInterface such as yii\db\Query or yii\db\Expression or yii\db\conditions\InCondition. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

39
        /** @scrutinizer ignore-call */ 
40
        $values = $expression->getValues();
Loading history...
40
41 1
        if ($column === []) {
42
            // no columns to test against
43
            return $operator === 'IN' ? '0=1' : '';
44
        }
45
46 1
        if ($values instanceof Query) {
47
            return $this->buildSubqueryInCondition($operator, $column, $values, $params);
48
        }
49
50 1
        if (!is_array($values) && !$values instanceof \Traversable) {
51
            // ensure values is an array
52
            $values = (array) $values;
53
        }
54
55 1
        if (is_array($column)) {
56 1
            if (count($column) > 1) {
57
                return $this->buildCompositeInCondition($operator, $column, $values, $params);
0 ignored issues
show
Bug introduced by
It seems like $values can also be of type Traversable; however, parameter $values of yii\db\conditions\InCond...dCompositeInCondition() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

57
                return $this->buildCompositeInCondition($operator, $column, /** @scrutinizer ignore-type */ $values, $params);
Loading history...
58
            }
59 1
            $column = reset($column);
60
        }
61
62 1
        if ($column instanceof \Traversable) {
63
            if (iterator_count($column) > 1) {
64
                return $this->buildCompositeInCondition($operator, $column, $values, $params);
65
            }
66
            $column->rewind();
0 ignored issues
show
Bug introduced by
The method rewind() does not exist on Traversable. It seems like you code against a sub-type of Traversable such as Yaf_Config_Simple or Yaf\Session or SimpleXMLElement or Yaf_Session or Yaf\Config\Simple or Yaf\Config\Ini or Iterator or Yaf_Config_Ini or MongoGridFSCursor or yii\test\ArrayFixture or yii\web\HeaderCollection or SplFixedArray or yii\test\BaseActiveFixture or yii\base\Model or yii\web\CookieCollection or yii\web\Session. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

66
            $column->/** @scrutinizer ignore-call */ 
67
                     rewind();
Loading history...
67
            $column = $column->current();
0 ignored issues
show
Bug introduced by
The method current() does not exist on Traversable. It seems like you code against a sub-type of Traversable such as IntlCodePointBreakIterator or Yaf_Config_Simple or Yaf\Session or SimpleXMLElement or IntlRuleBasedBreakIterator or Yaf_Session or Yaf\Config\Simple or Yaf\Config\Ini or Iterator or Yaf_Config_Ini or MongoGridFSCursor or yii\test\ArrayFixture or yii\web\HeaderCollection or SplFixedArray or yii\test\BaseActiveFixture or yii\base\Model or yii\web\CookieCollection or IntlBreakIterator or yii\web\Session. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

67
            /** @scrutinizer ignore-call */ 
68
            $column = $column->current();
Loading history...
68
        }
69
70 1
        if ($column instanceof Expression) {
71
            $column = $column->expression;
72
        }
73
74 1
        if (is_array($values)) {
75 1
            $rawValues = $values;
76
        } elseif ($values instanceof \Traversable) {
0 ignored issues
show
introduced by
$values is always a sub-type of Traversable.
Loading history...
77
            $rawValues = $this->getRawValuesFromTraversableObject($values);
78
        }
79
80 1
        $nullCondition = null;
81 1
        $nullConditionOperator = null;
82 1
        if (isset($rawValues) && in_array(null, $rawValues, true)) {
83
            $nullCondition = $this->getNullCondition($operator, $column);
84
            $nullConditionOperator = $operator === 'IN' ? 'OR' : 'AND';
85
        }
86
87 1
        $sqlValues = $this->buildValues($expression, $values, $params);
0 ignored issues
show
Bug introduced by
It seems like $values can also be of type Traversable; however, parameter $values of yii\db\conditions\InCond...nBuilder::buildValues() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

87
        $sqlValues = $this->buildValues($expression, /** @scrutinizer ignore-type */ $values, $params);
Loading history...
88 1
        if (empty($sqlValues)) {
89
            if ($nullCondition === null) {
90
                return $operator === 'IN' ? '0=1' : '';
91
            }
92
            return $nullCondition;
93
        }
94
95 1
        if (strpos($column, '(') === false) {
96 1
            $column = $this->queryBuilder->db->quoteColumnName($column);
97
        }
98 1
        if (count($sqlValues) > 1) {
99
            $sql = "$column $operator (" . implode(', ', $sqlValues) . ')';
100
        } else {
101 1
            $operator = $operator === 'IN' ? '=' : '<>';
102 1
            $sql = $column . $operator . reset($sqlValues);
103
        }
104
105 1
        return $nullCondition !== null && $nullConditionOperator !== null
106
            ? sprintf('%s %s %s', $sql, $nullConditionOperator, $nullCondition)
107 1
            : $sql;
108
    }
109
110
    /**
111
     * Builds $values to be used in [[InCondition]]
112
     *
113
     * @param ConditionInterface|InCondition $condition
114
     * @param array $values
115
     * @param array $params the binding parameters
116
     * @return array of prepared for SQL placeholders
117
     */
118 1
    protected function buildValues(ConditionInterface $condition, $values, &$params)
119
    {
120 1
        $sqlValues = [];
121 1
        $column = $condition->getColumn();
0 ignored issues
show
Bug introduced by
The method getColumn() does not exist on yii\db\conditions\ConditionInterface. It seems like you code against a sub-type of yii\db\conditions\ConditionInterface such as yii\db\conditions\InCondition or yii\db\conditions\BetweenCondition or yii\db\conditions\SimpleCondition. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

121
        /** @scrutinizer ignore-call */ 
122
        $column = $condition->getColumn();
Loading history...
122
123 1
        if (is_array($column)) {
124 1
            $column = reset($column);
125
        }
126
127 1
        if ($column instanceof \Traversable) {
128
            $column->rewind();
129
            $column = $column->current();
130
        }
131
132 1
        if ($column instanceof Expression) {
133
            $column = $column->expression;
134
        }
135
136 1
        foreach ($values as $i => $value) {
137 1
            if (is_array($value) || $value instanceof \ArrayAccess) {
138
                $value = isset($value[$column]) ? $value[$column] : null;
139
            }
140 1
            if ($value === null) {
141
                continue;
142 1
            } elseif ($value instanceof ExpressionInterface) {
143
                $sqlValues[$i] = $this->queryBuilder->buildExpression($value, $params);
144
            } else {
145 1
                $sqlValues[$i] = $this->queryBuilder->bindParam($value, $params);
146
            }
147
        }
148
149 1
        return $sqlValues;
150
    }
151
152
    /**
153
     * Builds SQL for IN condition.
154
     *
155
     * @param string $operator
156
     * @param array|string $columns
157
     * @param Query $values
158
     * @param array $params
159
     * @return string SQL
160
     */
161
    protected function buildSubqueryInCondition($operator, $columns, $values, &$params)
162
    {
163
        $sql = $this->queryBuilder->buildExpression($values, $params);
164
165
        if (is_array($columns)) {
166
            foreach ($columns as $i => $col) {
167
                if ($col instanceof Expression) {
168
                    $col = $col->expression;
169
                }
170
                if (strpos($col, '(') === false) {
171
                    $columns[$i] = $this->queryBuilder->db->quoteColumnName($col);
172
                }
173
            }
174
175
            return '(' . implode(', ', $columns) . ") $operator $sql";
176
        }
177
178
        if ($columns instanceof Expression) {
0 ignored issues
show
introduced by
$columns is never a sub-type of yii\db\Expression.
Loading history...
179
            $columns = $columns->expression;
180
        }
181
        if (strpos($columns, '(') === false) {
182
            $columns = $this->queryBuilder->db->quoteColumnName($columns);
183
        }
184
185
        return "$columns $operator $sql";
186
    }
187
188
    /**
189
     * Builds SQL for IN condition.
190
     *
191
     * @param string $operator
192
     * @param array|\Traversable $columns
193
     * @param array $values
194
     * @param array $params
195
     * @return string SQL
196
     */
197
    protected function buildCompositeInCondition($operator, $columns, $values, &$params)
198
    {
199
        $vss = [];
200
        foreach ($values as $value) {
201
            $vs = [];
202
            foreach ($columns as $column) {
203
                if ($column instanceof Expression) {
204
                    $column = $column->expression;
205
                }
206
                if (isset($value[$column])) {
207
                    $vs[] = $this->queryBuilder->bindParam($value[$column], $params);
208
                } else {
209
                    $vs[] = 'NULL';
210
                }
211
            }
212
            $vss[] = '(' . implode(', ', $vs) . ')';
213
        }
214
215
        if (empty($vss)) {
216
            return $operator === 'IN' ? '0=1' : '';
217
        }
218
219
        $sqlColumns = [];
220
        foreach ($columns as $i => $column) {
221
            if ($column instanceof Expression) {
222
                $column = $column->expression;
223
            }
224
            $sqlColumns[] = strpos($column, '(') === false ? $this->queryBuilder->db->quoteColumnName($column) : $column;
225
        }
226
227
        return '(' . implode(', ', $sqlColumns) . ") $operator (" . implode(', ', $vss) . ')';
228
    }
229
230
    /**
231
     * Builds is null/is not null condition for column based on operator
232
     *
233
     * @param string $operator
234
     * @param string $column
235
     * @return string is null or is not null condition
236
     * @since 2.0.31
237
     */
238
    protected function getNullCondition($operator, $column)
239
    {
240
        $column = $this->queryBuilder->db->quoteColumnName($column);
241
        if ($operator === 'IN') {
242
            return sprintf('%s IS NULL', $column);
243
        }
244
        return sprintf('%s IS NOT NULL', $column);
245
    }
246
247
    /**
248
     * @param \Traversable $traversableObject
249
     * @return array raw values
250
     * @since 2.0.31
251
     */
252
    protected function getRawValuesFromTraversableObject(\Traversable $traversableObject)
253
    {
254
        $rawValues = [];
255
        foreach ($traversableObject as $value) {
256
            if (is_array($value)) {
257
                $values = array_values($value);
258
                $rawValues = array_merge($rawValues, $values);
259
            } else {
260
                $rawValues[] = $value;
261
            }
262
        }
263
        return $rawValues;
264
    }
265
}
266