Passed
Pull Request — master (#303)
by
unknown
03:33
created

CompositeExpression::getIterator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 3
cp 0
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Pgsql;
6
7
use ArrayAccess;
8
use ArrayIterator;
9
use Countable;
10
use IteratorAggregate;
11
use Yiisoft\Db\Exception\InvalidConfigException;
12
use Yiisoft\Db\Expression\ExpressionInterface;
13
use Yiisoft\Db\Schema\ColumnSchemaInterface;
14
15
use function count;
16
17
/**
18
 * Represents a composite SQL expression.
19
 *
20
 * For example:
21
 *
22
 * ```php
23
 * new CompositeExpression(['price' => 10, 'currency_code' => 'USD']);
24
 * ```
25
 *
26
 * Will be encoded to `ROW(10, USD)`
27
 *
28
 * @template-implements ArrayAccess<string, mixed>
29
 * @template-implements IteratorAggregate<string>
30
 */
31
class CompositeExpression implements ExpressionInterface, ArrayAccess, Countable, IteratorAggregate
32
{
33
    /**
34
     * @param ColumnSchemaInterface[]|null $columns
35
     * @psalm-param array<string, ColumnSchemaInterface>|null $columns
36
     */
37 1
    public function __construct(
38
        private mixed $value = [],
39
        private string|null $type = null,
40
        private array|null $columns = null,
41 1
    ) {}
42
43
    /**
44
     * The composite type name.
45
     *
46
     * Defaults to `null` which means the type isn't explicitly specified.
47
     *
48
     * Note that in the case where a type isn't specified explicitly and DBMS can't guess it from the context, SQL error
49
     * will be raised.
50
     */
51 1
    public function getType(): string|null
52
    {
53 1
        return $this->type;
54
    }
55
56
    /**
57
     * @return ColumnSchemaInterface[]|null
58
     */
59 1
    public function getColumns(): array|null
60
    {
61 1
        return $this->columns;
62
    }
63
64
    /**
65
     * The composite type's content. It can be represented as an associative array of the composite type's column names
66
     * and values.
67
     */
68 1
    public function getValue(): mixed
69
    {
70 1
        return $this->value;
71
    }
72
73
    /**
74
     * Sort values according to `$columns` order and fill skipped items with default values
75
     */
76 1
    public function getNormalizedValue(): mixed
77
    {
78 1
        if ($this->columns === null || !is_array($this->value) || !is_string(array_key_first($this->value))) {
79
            return $this->value;
80
        }
81
82 1
        $value = [];
83
84 1
        foreach ($this->columns as $name => $column) {
85 1
            if (array_key_exists($name, $this->value)) {
86 1
                $value[$name] = $this->value[$name];
87
            } else {
88
                $value[$name] = $column->getDefaultValue();
89
            }
90
        }
91
92 1
        return $value;
93
    }
94
95
    /**
96
     * Whether an offset exists.
97
     *
98
     * @link https://php.net/manual/en/arrayaccess.offsetexists.php
99
     *
100
     * @param int|string $offset An offset to check for.
101
     *
102
     * @return bool Its `true` on success or `false` on failure.
103
     *
104
     * @throws InvalidConfigException If value is not an array.
105
     */
106
    public function offsetExists(mixed $offset): bool
107
    {
108
        $this->value = $this->validateValue($this->value);
109
        return array_key_exists($offset, $this->value);
110
    }
111
112
    /**
113
     * Offset to retrieve.
114
     *
115
     * @link https://php.net/manual/en/arrayaccess.offsetget.php
116
     *
117
     * @param int|string $offset The offset to retrieve.
118
     *
119
     * @return mixed Can return all value types.
120
     *
121
     * @throws InvalidConfigException If value is not an array.
122
     */
123
    public function offsetGet(mixed $offset): mixed
124
    {
125
        $this->value = $this->validateValue($this->value);
126
        return $this->value[$offset];
127
    }
128
129
    /**
130
     * Offset to set.
131
     *
132
     * @link https://php.net/manual/en/arrayaccess.offsetset.php
133
     *
134
     * @param int|string $offset The offset to assign the value to.
135
     * @param mixed $value The value to set.
136
     *
137
     * @throws InvalidConfigException If content value is not an array.
138
     */
139
    public function offsetSet(mixed $offset, mixed $value): void
140
    {
141
        $this->value = $this->validateValue($this->value);
142
        $this->value[$offset] = $value;
143
    }
144
145
    /**
146
     * Offset to unset.
147
     *
148
     * @param int|string $offset The offset to unset.
149
     *
150
     * @throws InvalidConfigException If value is not an array.
151
     *
152
     * @link https://php.net/manual/en/arrayaccess.offsetunset.php
153
     */
154
    public function offsetUnset(mixed $offset): void
155
    {
156
        $this->value = $this->validateValue($this->value);
157
        unset($this->value[$offset]);
158
    }
159
160
    /**
161
     * Count elements of the composite type's content.
162
     *
163
     * @link https://php.net/manual/en/countable.count.php
164
     *
165
     * @return int The custom count as an integer.
166
     */
167
    public function count(): int
168
    {
169
        return count((array) $this->value);
170
    }
171
172
    /**
173
     * Retrieve an external iterator.
174
     *
175
     * @link https://php.net/manual/en/iteratoraggregate.getiterator.php
176
     *
177
     * @throws InvalidConfigException If value is not an array.
178
     *
179
     * @return ArrayIterator An instance of an object implementing `Iterator` or `Traversable`.
180
     */
181
    public function getIterator(): ArrayIterator
182
    {
183
        $this->value = $this->validateValue($this->value);
184
        return new ArrayIterator($this->value);
185
    }
186
187
    /**
188
     * Validates the value of the composite expression is an array.
189
     *
190
     * @throws InvalidConfigException If value is not an array.
191
     */
192
    private function validateValue(mixed $value): array
193
    {
194
        if (!is_array($value)) {
195
            throw new InvalidConfigException('The CompositeExpression value must be an array.');
196
        }
197
198
        return $value;
199
    }
200
}
201