Passed
Pull Request — master (#284)
by
unknown
04:07 queued 40s
created

ColumnSchema::phpTypecast()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

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