Passed
Pull Request — master (#153)
by
unknown
03:15
created

ColumnSchema::getPhpArrayType()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 1
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 2
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Pgsql;
6
7
use JsonException;
8
use Yiisoft\Db\Expression\ArrayExpression;
9
use Yiisoft\Db\Expression\ExpressionInterface;
10
use Yiisoft\Db\Expression\JsonExpression;
11
use Yiisoft\Db\Schema\ColumnSchema as AbstractColumnSchema;
12
use Yiisoft\Db\Schema\Schema as AbstractSchema;
13
use function in_array;
14
use function is_bool;
15
use function is_float;
16
use function is_string;
17
use function json_decode;
18
use function strtolower;
19
use const JSON_THROW_ON_ERROR;
20
21
/**
22
 * The class ColumnSchema for PostgreSQL database.
23
 */
24
final class ColumnSchema extends AbstractColumnSchema
25
{
26
    /**
27
     * @var int the dimension of array. Defaults to 0, means this column is not an array.
28
     */
29
    private int $dimension = 0;
30
31
    /**
32
     * @var string|null name of associated sequence if column is auto-incremental.
33
     */
34
    private ?string $sequenceName = null;
35
36
    /**
37
     * Return type of PgSql array values
38
     *
39
     * @return string|null
40
     */
41 3
    public function getPhpArrayType(): ?string
42
    {
43 3
        return $this->dimension > 0 ? parent::getPhpType() : null;
44
    }
45
46 3
    public function getPhpType(): ?string
47
    {
48 3
        return $this->dimension > 0 ? AbstractSchema::PHP_TYPE_ARRAY : parent::getPhpType();
49
    }
50
51
    /**
52
     * Converts the input value according to {@see type} and {@see dbType} for use in a db query.
53
     *
54
     * If the value is null or an {@see Expression}, it will not be converted.
55
     *
56
     * @param mixed $value input value
57
     *
58
     * @return mixed converted value. This may also be an array containing the value as the first element and the PDO
59
     * type as the second element.
60
     */
61 58
    public function dbTypecast(mixed $value): mixed
62
    {
63 58
        if ($value === null) {
64 6
            return null;
65
        }
66
67 58
        if ($value instanceof ExpressionInterface) {
68 13
            return $value;
69
        }
70
71 56
        if ($this->dimension > 0) {
72 1
            return new ArrayExpression($value, $this->getDbType(), $this->dimension);
73
        }
74
75 56
        if (in_array($this->getDbType(), [AbstractSchema::TYPE_JSON, Schema::TYPE_JSONB], true)) {
76 1
            return new JsonExpression($value, $this->getDbType());
77
        }
78
79 56
        return $this->typecast($value);
80
    }
81
82
    /**
83
     * Converts the input value according to {@see phpType} after retrieval from the database.
84
     *
85
     * If the value is null or an {@see Expression}, it will not be converted.
86
     *
87
     * @param mixed $value input value
88
     *
89
     *@throws JsonException
90
     *
91
     * @return mixed converted value
92
     */
93 33
    public function phpTypecast(mixed $value): mixed
94
    {
95 33
        if ($this->dimension > 0) {
96 1
            if ($value === null) {
97 1
                return null;
98
            }
99
100 1
            if (is_string($value)) {
101 1
                return $this->getArrayParser()->parse($value);
102
            }
103
104
            return $value;
105
        }
106
107 33
        return $this->phpTypecastValue($value);
108
    }
109
110
    /**
111
     * Cast mixed value to PHP boolean type
112
     *
113
     * @param mixed $value
114
     * @return bool|null
115
     */
116 2
    public static function castBooleanValue(mixed $value): ?bool
117
    {
118 2
        if (is_bool($value) || $value === null) {
119 1
            return $value;
120
        }
121
        /** @var mixed $value */
122 1
        $value = is_string($value) ? strtolower($value) : $value;
123
124 1
        return match ($value) {
125 1
            't', 'true' => true,
126 1
            'f', 'false' => false,
127 1
            default => (bool) $value,
128
        };
129
    }
130
131
    /**
132
     * Casts $value after retrieving from the DBMS to PHP representation.
133
     *
134
     * @param mixed $value
135
     *
136
     * @throws JsonException
137
     *
138
     * @return mixed
139
     */
140 33
    protected function phpTypecastValue(mixed $value): mixed
141
    {
142 33
        if ($value === null) {
143
            return null;
144
        }
145
146 33
        if ($this->dimension > 0) {
147
            return match ($this->getPhpArrayType()) {
148
                AbstractSchema::PHP_TYPE_INTEGER => is_int($value) ? $value : (int) $value,
149
                AbstractSchema::PHP_TYPE_DOUBLE => is_float($value) ? $value : (float) $value,
150
                AbstractSchema::PHP_TYPE_BOOLEAN => self::castBooleanValue($value),
151
                AbstractSchema::PHP_TYPE_ARRAY => json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR),
152
                default => $value,
153
            };
154
        }
155
156 33
        return match ($this->getType()) {
157 2
            AbstractSchema::TYPE_BOOLEAN => self::castBooleanValue($value),
158 29
            AbstractSchema::TYPE_JSON => json_decode((string) $value, true, 512, JSON_THROW_ON_ERROR),
159 33
            default => parent::phpTypecast($value),
160
        };
161
    }
162
163
    /**
164
     * Creates instance of ArrayParser.
165
     *
166
     * @return ArrayParser
167
     */
168 1
    protected function getArrayParser(): ArrayParser
169
    {
170 1
        return (new ArrayParser)->withTypeCast($this->getPhpArrayType());
171
    }
172
173
    /**
174
     * @return int Get the dimension of array. Defaults to 0, means this column is not an array.
175
     */
176 1
    public function getDimension(): int
177
    {
178 1
        return $this->dimension;
179
    }
180
181
    /**
182
     * @return string|null name of associated sequence if column is auto-incremental.
183
     */
184 73
    public function getSequenceName(): ?string
185
    {
186 73
        return $this->sequenceName;
187
    }
188
189
    /**
190
     * Set dimension of array. Defaults to 0, means this column is not an array.
191
     */
192 106
    public function dimension(int $dimension): void
193
    {
194 106
        $this->dimension = $dimension;
195
    }
196
197
    /**
198
     * Set name of associated sequence if column is auto-incremental.
199
     */
200 70
    public function sequenceName(?string $sequenceName): void
201
    {
202 70
        $this->sequenceName = $sequenceName;
203
    }
204
}
205