Passed
Pull Request — master (#808)
by Sergei
15:21 queued 13:01
created

ColumnFactory   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 221
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 123
c 1
b 0
f 0
dl 0
loc 221
rs 10
wmc 27

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getBuilderClass() 0 3 1
A fromPhpType() 0 16 1
A getPhpType() 0 18 1
B fromType() 0 19 8
B fromDefinition() 0 45 9
A __construct() 0 4 1
A getDbType() 0 23 1
A getType() 0 7 2
A fromDbType() 0 11 2
A getTypeFromPhp() 0 10 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\Schema\Column;
6
7
use Yiisoft\Db\Schema\SchemaInterface;
8
9
use function count;
10
use function explode;
11
use function in_array;
12
use function preg_match;
13
use function preg_match_all;
14
use function str_contains;
15
use function str_replace;
16
use function strlen;
17
use function strtolower;
18
use function substr;
19
use function trim;
20
21
use const PHP_INT_SIZE;
22
23
/**
24
 * @psalm-param array<string, class-string<ColumnInterface>> $fromDbType
25
 * @psalm-param array<string, class-string<ColumnInterface>> $fromType
26
 */
27
class ColumnFactory implements ColumnFactoryInterface
28
{
29
    private const BUILDERS = [
30
        'pk', 'upk', 'bigpk', 'ubigpk', 'uuidpk', 'uuidpkseq',
31
    ];
32
33
    private const TYPES = [
34
        SchemaInterface::TYPE_UUID,
35
        SchemaInterface::TYPE_CHAR,
36
        SchemaInterface::TYPE_STRING,
37
        SchemaInterface::TYPE_TEXT,
38
        SchemaInterface::TYPE_BINARY,
39
        SchemaInterface::TYPE_BIT,
40
        SchemaInterface::TYPE_BOOLEAN,
41
        SchemaInterface::TYPE_TINYINT,
42
        SchemaInterface::TYPE_SMALLINT,
43
        SchemaInterface::TYPE_INTEGER,
44
        SchemaInterface::TYPE_BIGINT,
45
        SchemaInterface::TYPE_FLOAT,
46
        SchemaInterface::TYPE_DOUBLE,
47
        SchemaInterface::TYPE_DECIMAL,
48
        SchemaInterface::TYPE_MONEY,
49
        SchemaInterface::TYPE_DATETIME,
50
        SchemaInterface::TYPE_TIMESTAMP,
51
        SchemaInterface::TYPE_TIME,
52
        SchemaInterface::TYPE_DATE,
53
        SchemaInterface::TYPE_JSON,
54
        SchemaInterface::TYPE_ARRAY,
55
        SchemaInterface::TYPE_COMPOSITE,
56
    ];
57
58
    public function __construct(
59
        private array $fromDbType = [],
60
        private array $fromType = [],
61
    ) {
62
    }
63
64
    public function fromDbType(string $dbType, array $info = []): ColumnInterface
65
    {
66
        $info['db_type'] = $dbType;
67
        $type = $info['type'] ?? $this->getType($dbType);
68
69
        if (isset($this->fromDbType[$dbType])) {
70
            $phpType = $info['php_type'] ?? $this->getPhpType($type);
71
            return (new $this->fromDbType[$dbType]($type, $phpType))->load($info);
72
        }
73
74
        return $this->fromType($type, $info);
75
    }
76
77
    public function fromDefinition(string $definition, array $info = []): ColumnInterface
78
    {
79
        preg_match('/^(\w*)(?:\(([^)]+)\))?\s*/', $definition, $matches);
80
81
        $dbType = strtolower($matches[1]);
82
83
        if (isset($matches[2])) {
84
            if ($dbType === 'enum') {
85
                preg_match_all("/'([^']*)'/", $matches[2], $values);
86
87
                $info['values'] = $values[1];
88
            } else {
89
                $values = explode(',', $matches[2]);
90
                $info['size'] = (int) $values[0];
91
92
                if (count($values) === 2) {
93
                    $info['scale'] = (int) $values[1];
94
                }
95
            }
96
        }
97
98
        $extra = substr($definition, strlen($matches[0]));
99
100
        if (str_contains($extra, 'unsigned')) {
101
            $info['unsigned'] = true;
102
            $extra = trim(str_replace('unsigned', '', $extra));
103
        }
104
105
        if ($extra !== '') {
106
            if (empty($info['extra'])) {
107
                $info['extra'] = $extra;
108
            } else {
109
                $info['extra'] = $extra . ' ' . $info['extra'];
110
            }
111
        }
112
113
        if (in_array($dbType, self::BUILDERS, true)) {
114
            return $this->getBuilderClass()::$dbType()->load($info);
115
        }
116
117
        if (in_array($dbType, self::TYPES, true)) {
118
            return $this->fromType($dbType, $info);
119
        }
120
121
        return $this->fromDbType($dbType, $info);
122
    }
123
124
    public function fromPhpType(string $phpType, array $info = []): ColumnInterface
125
    {
126
        $type = $info['type'] ?? $this->getTypeFromPhp($phpType);
127
128
        $column = match ($phpType) {
129
            SchemaInterface::PHP_TYPE_INTEGER => new IntegerColumn($type, $phpType),
130
            SchemaInterface::PHP_TYPE_DOUBLE => new DoubleColumn($type, $phpType),
131
            SchemaInterface::PHP_TYPE_BOOLEAN => new BooleanColumn($type, $phpType),
132
            SchemaInterface::PHP_TYPE_RESOURCE => new BinaryColumn($type, $phpType),
133
            SchemaInterface::PHP_TYPE_ARRAY => new JsonColumn($type, $phpType),
134
            default => new StringColumn($type, $phpType),
135
        };
136
137
        $column->load($info);
138
139
        return $column;
140
    }
141
142
    public function fromType(string $type, array $info = []): ColumnInterface
143
    {
144
        $info['type'] = $type;
145
        $phpType = $info['php_type'] ?? $this->getPhpType($type);
146
147
        if (isset($this->fromType[$type])) {
148
            return (new $this->fromType[$type]($type, $phpType))->load($info);
149
        }
150
151
        $isUnsigned = !empty($info['unsigned']);
152
153
        if (
154
            $isUnsigned && PHP_INT_SIZE === 4 && $type === SchemaInterface::TYPE_INTEGER
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: ($isUnsigned && PHP_INT_...aInterface::TYPE_BIGINT, Probably Intended Meaning: $isUnsigned && PHP_INT_S...Interface::TYPE_BIGINT)
Loading history...
155
            || ($isUnsigned || PHP_INT_SIZE !== 8) && $type === SchemaInterface::TYPE_BIGINT
156
        ) {
157
            return (new BigIntColumn($type, $phpType))->load($info);
158
        }
159
160
        return $this->fromPhpType($phpType, $info);
161
    }
162
163
    public function getBuilderClass(): string
164
    {
165
        return ColumnBuilder::class;
166
    }
167
168
    /**
169
     * Get the abstract database type from a database column type.
170
     *
171
     * @param string $dbType The database column type.
172
     *
173
     * @return string The abstract database type.
174
     */
175
    protected function getType(string $dbType): string
176
    {
177
        if (in_array($dbType, self::TYPES, true)) {
178
            return $dbType;
179
        }
180
181
        return SchemaInterface::TYPE_STRING;
182
    }
183
184
    /**
185
     * Get the PHP type from an abstract database type.
186
     *
187
     * @param string $type The abstract database type.
188
     *
189
     * @return string The PHP type name.
190
     */
191
    protected function getPhpType(string $type): string
192
    {
193
        return match ($type) {
194
            // abstract type => php type
195
            SchemaInterface::TYPE_BOOLEAN => SchemaInterface::PHP_TYPE_BOOLEAN,
196
            SchemaInterface::TYPE_BIT => SchemaInterface::PHP_TYPE_INTEGER,
197
            SchemaInterface::TYPE_TINYINT => SchemaInterface::PHP_TYPE_INTEGER,
198
            SchemaInterface::TYPE_SMALLINT => SchemaInterface::PHP_TYPE_INTEGER,
199
            SchemaInterface::TYPE_INTEGER => SchemaInterface::PHP_TYPE_INTEGER,
200
            SchemaInterface::TYPE_BIGINT => SchemaInterface::PHP_TYPE_INTEGER,
201
            SchemaInterface::TYPE_DECIMAL => SchemaInterface::PHP_TYPE_DOUBLE,
202
            SchemaInterface::TYPE_FLOAT => SchemaInterface::PHP_TYPE_DOUBLE,
203
            SchemaInterface::TYPE_DOUBLE => SchemaInterface::PHP_TYPE_DOUBLE,
204
            SchemaInterface::TYPE_BINARY => SchemaInterface::PHP_TYPE_RESOURCE,
205
            SchemaInterface::TYPE_JSON => SchemaInterface::PHP_TYPE_ARRAY,
206
            SchemaInterface::TYPE_ARRAY => SchemaInterface::PHP_TYPE_ARRAY,
207
            SchemaInterface::TYPE_COMPOSITE => SchemaInterface::PHP_TYPE_ARRAY,
208
            default => SchemaInterface::PHP_TYPE_STRING,
209
        };
210
    }
211
212
    protected function getTypeFromPhp(string $phpType): string
213
    {
214
        return match ($phpType) {
215
            // php type => abstract type
216
            SchemaInterface::PHP_TYPE_INTEGER => SchemaInterface::TYPE_INTEGER,
217
            SchemaInterface::PHP_TYPE_BOOLEAN => SchemaInterface::TYPE_BOOLEAN,
218
            SchemaInterface::PHP_TYPE_DOUBLE => SchemaInterface::TYPE_DOUBLE,
219
            SchemaInterface::PHP_TYPE_RESOURCE => SchemaInterface::TYPE_BINARY,
220
            SchemaInterface::PHP_TYPE_ARRAY => SchemaInterface::TYPE_JSON,
221
            default => SchemaInterface::TYPE_STRING,
222
        };
223
    }
224
225
    protected function getDbType(string $type): string
226
    {
227
        return match ($type) {
228
            SchemaInterface::TYPE_CHAR => 'char',
229
            SchemaInterface::TYPE_STRING => 'varchar',
230
            SchemaInterface::TYPE_TEXT => 'text',
231
            SchemaInterface::TYPE_TINYINT => 'tinyint',
232
            SchemaInterface::TYPE_SMALLINT => 'smallint',
233
            SchemaInterface::TYPE_INTEGER => 'integer',
234
            SchemaInterface::TYPE_BIGINT => 'bigint',
235
            SchemaInterface::TYPE_FLOAT => 'float',
236
            SchemaInterface::TYPE_DOUBLE => 'double',
237
            SchemaInterface::TYPE_DECIMAL => 'decimal',
238
            SchemaInterface::TYPE_DATETIME => 'datetime',
239
            SchemaInterface::TYPE_TIMESTAMP => 'timestamp',
240
            SchemaInterface::TYPE_TIME => 'time',
241
            SchemaInterface::TYPE_DATE => 'date',
242
            SchemaInterface::TYPE_BINARY => 'blob',
243
            SchemaInterface::TYPE_BOOLEAN => 'bit',
244
            SchemaInterface::TYPE_MONEY => 'decimal',
245
            SchemaInterface::TYPE_JSON => 'jsonb',
246
            SchemaInterface::TYPE_UUID => 'binary',
247
            default => 'varchar',
248
        };
249
    }
250
}
251