Passed
Push — master ( 244aa0...c1114e )
by Wilmer
27:09 queued 12:07
created

ArrayExpressionBuilder::buildPlaceholders()   B

Complexity

Conditions 9
Paths 7

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 19
nc 7
nop 2
dl 0
loc 34
rs 8.0555
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Pgsql;
6
7
use Traversable;
8
use Yiisoft\Db\Exception\Exception;
9
use Yiisoft\Db\Exception\InvalidArgumentException;
10
use Yiisoft\Db\Exception\InvalidConfigException;
11
use Yiisoft\Db\Exception\NotSupportedException;
12
use Yiisoft\Db\Expression\ArrayExpression;
13
use Yiisoft\Db\Expression\ExpressionBuilderInterface;
14
use Yiisoft\Db\Expression\ExpressionBuilderTrait;
15
use Yiisoft\Db\Expression\ExpressionInterface;
16
use Yiisoft\Db\Expression\JsonExpression;
17
use Yiisoft\Db\Query\Query;
18
19
use function get_class;
20
use function implode;
21
use function in_array;
22
use function is_array;
23
use function str_repeat;
24
25
final class ArrayExpressionBuilder implements ExpressionBuilderInterface
26
{
27
    use ExpressionBuilderTrait;
28
29
    /**
30
     * Method builds the raw SQL from the $expression that will not be additionally escaped or quoted.
31
     *
32
     * @param ExpressionInterface $expression the expression to be built.
33
     * @param array $params the binding parameters.
34
     *
35
     * @throws Exception
36
     * @throws InvalidArgumentException
37
     * @throws InvalidConfigException
38
     * @throws NotSupportedException
39
     *
40
     * @return string the raw SQL that will not be additionally escaped or quoted.
41
     */
42
    public function build(ExpressionInterface $expression, array &$params = []): string
43
    {
44
        $value = $expression->getValue();
0 ignored issues
show
Bug introduced by
The method getValue() does not exist on Yiisoft\Db\Expression\ExpressionInterface. It seems like you code against a sub-type of Yiisoft\Db\Expression\ExpressionInterface such as Yiisoft\Db\Pdo\PdoValue or Yiisoft\Db\Expression\ArrayExpression or Yiisoft\Db\Expression\JsonExpression or Yiisoft\Db\Query\Conditions\SimpleCondition or Yiisoft\Db\Query\Conditi...BetweenColumnsCondition. ( Ignorable by Annotation )

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

44
        /** @scrutinizer ignore-call */ 
45
        $value = $expression->getValue();
Loading history...
45
        if ($value === null) {
46
            return 'NULL';
47
        }
48
49
        if ($value instanceof Query) {
50
            [$sql, $params] = $this->queryBuilder->build($value, $params);
51
            return $this->buildSubqueryArray($sql, $expression);
52
        }
53
54
        $placeholders = $this->buildPlaceholders($expression, $params);
55
56
        return 'ARRAY[' . implode(', ', $placeholders) . ']' . $this->getTypehint($expression);
57
    }
58
59
    /**
60
     * Builds placeholders array out of $expression values.
61
     *
62
     * @param ExpressionInterface $expression
63
     * @param array $params the binding parameters.
64
     *
65
     * @throws Exception
66
     * @throws InvalidArgumentException
67
     * @throws InvalidConfigException
68
     * @throws NotSupportedException
69
     *
70
     * @return array
71
     */
72
    protected function buildPlaceholders(ExpressionInterface $expression, &$params): array
73
    {
74
        $value = $expression->getValue();
75
76
        $placeholders = [];
77
        if ($value === null || (!is_array($value) && !$value instanceof Traversable)) {
78
            return $placeholders;
79
        }
80
81
        if ($expression->getDimension() > 1) {
0 ignored issues
show
Bug introduced by
The method getDimension() does not exist on Yiisoft\Db\Expression\ExpressionInterface. It seems like you code against a sub-type of Yiisoft\Db\Expression\ExpressionInterface such as Yiisoft\Db\Expression\ArrayExpression. ( Ignorable by Annotation )

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

81
        if ($expression->/** @scrutinizer ignore-call */ getDimension() > 1) {
Loading history...
82
            foreach ($value as $item) {
83
                $placeholders[] = $this->build($this->unnestArrayExpression($expression, $item), $params);
84
            }
85
            return $placeholders;
86
        }
87
88
        foreach ($value as $item) {
89
            if ($item instanceof Query) {
90
                [$sql, $params] = $this->queryBuilder->build($item, $params);
91
                $placeholders[] = $this->buildSubqueryArray($sql, $expression);
92
                continue;
93
            }
94
95
            $item = $this->typecastValue($expression, $item);
96
97
            if ($item instanceof ExpressionInterface) {
98
                $placeholders[] = $this->queryBuilder->buildExpression($item, $params);
99
                continue;
100
            }
101
102
            $placeholders[] = $this->queryBuilder->bindParam($item, $params);
103
        }
104
105
        return $placeholders;
106
    }
107
108
    private function unnestArrayExpression(ArrayExpression $expression, $value): ArrayExpression
109
    {
110
        $expressionClass = get_class($expression);
111
112
        return new $expressionClass($value, $expression->getType(), $expression->getDimension() - 1);
113
    }
114
115
    protected function getTypeHint(ArrayExpression $expression): string
116
    {
117
        if ($expression->getType() === null) {
118
            return '';
119
        }
120
121
        $result = '::' . $expression->getType();
122
        $result .= str_repeat('[]', $expression->getDimension());
123
124
        return $result;
125
    }
126
127
    /**
128
     * Build an array expression from a subquery SQL.
129
     *
130
     * @param string $sql the subquery SQL.
131
     * @param ArrayExpression $expression
132
     *
133
     * @return string the subquery array expression.
134
     */
135
    protected function buildSubqueryArray(string $sql, ArrayExpression $expression): string
136
    {
137
        return 'ARRAY(' . $sql . ')' . $this->getTypeHint($expression);
138
    }
139
140
    /**
141
     * Casts $value to use in $expression.
142
     *
143
     * @param ArrayExpression $expression
144
     * @param mixed $value
145
     *
146
     * @return int|JsonExpression
147
     */
148
    protected function typecastValue(ArrayExpression $expression, $value)
149
    {
150
        if ($value instanceof ExpressionInterface) {
151
            return $value;
152
        }
153
154
        if (in_array($expression->getType(), [Schema::TYPE_JSON, Schema::TYPE_JSONB], true)) {
155
            return new JsonExpression($value);
156
        }
157
158
        return $value;
159
    }
160
}
161