Passed
Push — master ( bfbdfe...8da22b )
by Def
03:38
created

ColumnSchema::phpTypecastValue()   B

Complexity

Conditions 7
Paths 7

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 7

Importance

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