Passed
Pull Request — master (#808)
by Sergei
02:31
created

ColumnFactory::fromDefinition()   B

Complexity

Conditions 10
Paths 72

Size

Total Lines 45
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 25
c 2
b 0
f 0
nc 72
nop 2
dl 0
loc 45
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 explode;
10
use function preg_match;
11
use function preg_match_all;
12
use function str_contains;
13
use function str_ireplace;
14
use function strlen;
15
use function strtolower;
16
use function substr;
17
use function trim;
18
19
use const PHP_INT_SIZE;
20
21
/**
22
 * @psalm-param array<string, class-string<ColumnInterface>> $fromDbType
23
 * @psalm-param array<string, class-string<ColumnInterface>> $fromType
24
 */
25
class ColumnFactory implements ColumnFactoryInterface
26
{
27
    private const TYPE_MAP = [
28
        'uuid' => SchemaInterface::TYPE_UUID,
29
        'char' => SchemaInterface::TYPE_CHAR,
30
        'varchar' => SchemaInterface::TYPE_STRING,
31
        'text' => SchemaInterface::TYPE_TEXT,
32
        'binary' => SchemaInterface::TYPE_BINARY,
33
        'boolean' => SchemaInterface::TYPE_BOOLEAN,
34
        'tinyint' => SchemaInterface::TYPE_TINYINT,
35
        'smallint' => SchemaInterface::TYPE_SMALLINT,
36
        'integer' => SchemaInterface::TYPE_INTEGER,
37
        'bigint' => SchemaInterface::TYPE_BIGINT,
38
        'float' => SchemaInterface::TYPE_FLOAT,
39
        'double' => SchemaInterface::TYPE_DOUBLE,
40
        'decimal' => SchemaInterface::TYPE_DECIMAL,
41
        'money' => SchemaInterface::TYPE_MONEY,
42
        'datetime' => SchemaInterface::TYPE_DATETIME,
43
        'timestamp' => SchemaInterface::TYPE_TIMESTAMP,
44
        'time' => SchemaInterface::TYPE_TIME,
45
        'date' => SchemaInterface::TYPE_DATE,
46
        'json' => SchemaInterface::TYPE_JSON,
47
    ];
48
49
    public function __construct(
50
        private string $columnBuilderClass = ColumnBuilder::class,
51
        private array $fromDbType = [],
52
        private array $fromType = [],
53
    ) {
54
    }
55
56
    public function fromDbType(string $dbType, array $info = []): ColumnInterface
57
    {
58
        $info['db_type'] = $dbType;
59
        $type = $info['type'] ?? $this->getTypeFromDb($dbType);
60
61
        if (isset($this->fromDbType[$dbType])) {
62
            $phpType = $info['php_type'] ?? $this->getPhpType($type);
63
            return (new $this->fromDbType[$dbType]($type, $phpType))->load($info);
64
        }
65
66
        return $this->fromType($type, $info);
67
    }
68
69
    public function fromDefinition(string $definition, array $info = []): ColumnInterface
70
    {
71
        preg_match('/^(\w*)(?:\(([^)]+)\))?\s*/', $definition, $matches);
72
73
        $dbType = strtolower($matches[1]);
74
75
        if (isset($matches[2])) {
76
            if ($dbType === 'enum') {
77
                preg_match_all("/'([^']*)'/", $matches[2], $values);
78
79
                $info['values'] = $values[1];
80
            } else {
81
                $values = explode(',', $matches[2]);
82
                $info['size'] = (int) $values[0];
83
84
                if (isset($values[1])) {
85
                    $info['scale'] = (int) $values[1];
86
                }
87
            }
88
        }
89
90
        $extra = substr($definition, strlen($matches[0]));
91
92
        if (!empty($extra) && str_contains(strtolower($extra), 'unsigned')) {
93
            $info['unsigned'] = true;
94
            $extra = trim(str_ireplace('unsigned', '', $extra));
0 ignored issues
show
Bug introduced by
It seems like str_ireplace('unsigned', '', $extra) can also be of type array; however, parameter $string of trim() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

94
            $extra = trim(/** @scrutinizer ignore-type */ str_ireplace('unsigned', '', $extra));
Loading history...
95
        }
96
97
        if (!empty($extra)) {
98
            if (empty($info['extra'])) {
99
                $info['extra'] = $extra;
100
            } else {
101
                $info['extra'] = $extra . ' ' . $info['extra'];
102
            }
103
        }
104
105
        if ($this->isDbType($dbType)) {
106
            return $this->fromDbType($dbType, $info);
107
        }
108
109
        if ($this->isBuilder($dbType)) {
110
            return $this->columnBuilderClass::$dbType()->load($info);
111
        }
112
113
        return $this->fromDbType($dbType, $info);
114
    }
115
116
    public function fromPhpType(string $phpType, array $info = []): ColumnInterface
117
    {
118
        $type = $info['type'] ?? $this->getTypeFromPhp($phpType);
119
120
        $column = match ($phpType) {
121
            SchemaInterface::PHP_TYPE_INTEGER => new IntegerColumn($type, $phpType),
122
            SchemaInterface::PHP_TYPE_DOUBLE => new DoubleColumn($type, $phpType),
123
            SchemaInterface::PHP_TYPE_BOOLEAN => new BooleanColumn($type, $phpType),
124
            SchemaInterface::PHP_TYPE_RESOURCE => new BinaryColumn($type, $phpType),
125
            SchemaInterface::PHP_TYPE_ARRAY => new JsonColumn($type, $phpType),
126
            default => new StringColumn($type, $phpType),
127
        };
128
129
        $column->load($info);
130
131
        return $column;
132
    }
133
134
    public function fromType(string $type, array $info = []): ColumnInterface
135
    {
136
        $info['type'] = $type;
137
        $phpType = $info['php_type'] ?? $this->getPhpType($type);
138
139
        if (isset($this->fromType[$type])) {
140
            return (new $this->fromType[$type]($type, $phpType))->load($info);
141
        }
142
143
        $isUnsigned = !empty($info['unsigned']);
144
145
        if (
146
            PHP_INT_SIZE !== 8 && $isUnsigned && $type === SchemaInterface::TYPE_INTEGER
0 ignored issues
show
introduced by
Consider adding parentheses for clarity. Current Interpretation: (PHP_INT_SIZE !== 8 && $...aInterface::TYPE_BIGINT, Probably Intended Meaning: PHP_INT_SIZE !== 8 && $i...Interface::TYPE_BIGINT)
Loading history...
147
            || (PHP_INT_SIZE !== 8 || $isUnsigned) && $type === SchemaInterface::TYPE_BIGINT
148
        ) {
149
            return (new BigIntColumn($type, $phpType))->load($info);
150
        }
151
152
        return $this->fromPhpType($phpType, $info);
153
    }
154
155
    /**
156
     * Get the PHP type from an abstract database type.
157
     *
158
     * @param string $type The abstract database type.
159
     *
160
     * @return string The PHP type name.
161
     */
162
    protected function getPhpType(string $type): string
163
    {
164
        return match ($type) {
165
            // abstract type => php type
166
            SchemaInterface::TYPE_BOOLEAN => SchemaInterface::PHP_TYPE_BOOLEAN,
167
            SchemaInterface::TYPE_TINYINT => SchemaInterface::PHP_TYPE_INTEGER,
168
            SchemaInterface::TYPE_SMALLINT => SchemaInterface::PHP_TYPE_INTEGER,
169
            SchemaInterface::TYPE_INTEGER => SchemaInterface::PHP_TYPE_INTEGER,
170
            SchemaInterface::TYPE_BIGINT => SchemaInterface::PHP_TYPE_INTEGER,
171
            SchemaInterface::TYPE_DECIMAL => SchemaInterface::PHP_TYPE_DOUBLE,
172
            SchemaInterface::TYPE_FLOAT => SchemaInterface::PHP_TYPE_DOUBLE,
173
            SchemaInterface::TYPE_DOUBLE => SchemaInterface::PHP_TYPE_DOUBLE,
174
            SchemaInterface::TYPE_BINARY => SchemaInterface::PHP_TYPE_RESOURCE,
175
            SchemaInterface::TYPE_JSON => SchemaInterface::PHP_TYPE_ARRAY,
176
            default => SchemaInterface::PHP_TYPE_STRING,
177
        };
178
    }
179
180
    /**
181
     * Get the abstract database type from a database column type.
182
     *
183
     * @param string $dbType The database column type.
184
     *
185
     * @return string The abstract database type.
186
     */
187
    protected function getTypeFromDb(string $dbType): string
188
    {
189
        return self::TYPE_MAP[$dbType] ?? SchemaInterface::TYPE_STRING;
190
    }
191
192
    protected function getTypeFromPhp(string $phpType): string
193
    {
194
        return match ($phpType) {
195
            // php type => abstract type
196
            SchemaInterface::PHP_TYPE_INTEGER => SchemaInterface::TYPE_INTEGER,
197
            SchemaInterface::PHP_TYPE_BOOLEAN => SchemaInterface::TYPE_BOOLEAN,
198
            SchemaInterface::PHP_TYPE_DOUBLE => SchemaInterface::TYPE_DOUBLE,
199
            SchemaInterface::PHP_TYPE_RESOURCE => SchemaInterface::TYPE_BINARY,
200
            SchemaInterface::PHP_TYPE_ARRAY => SchemaInterface::TYPE_JSON,
201
            default => SchemaInterface::TYPE_STRING,
202
        };
203
    }
204
205
    protected function isBuilder(string $dbType): bool
206
    {
207
        return match ($dbType) {
208
            'pk',
209
            'upk',
210
            'bigpk',
211
            'ubigpk',
212
            'uuidpk',
213
            'uuidpkseq' => true,
214
            default => $this->isType($dbType),
215
        };
216
    }
217
218
    protected function isDbType(string $dbType): bool
219
    {
220
        return isset(self::TYPE_MAP[$dbType]);
221
    }
222
223
    protected function isType(string $dbType): bool
224
    {
225
        return match ($dbType) {
226
            SchemaInterface::TYPE_UUID,
227
            SchemaInterface::TYPE_CHAR,
228
            SchemaInterface::TYPE_STRING,
229
            SchemaInterface::TYPE_TEXT,
230
            SchemaInterface::TYPE_BINARY,
231
            SchemaInterface::TYPE_BOOLEAN,
232
            SchemaInterface::TYPE_TINYINT,
233
            SchemaInterface::TYPE_SMALLINT,
234
            SchemaInterface::TYPE_INTEGER,
235
            SchemaInterface::TYPE_BIGINT,
236
            SchemaInterface::TYPE_FLOAT,
237
            SchemaInterface::TYPE_DOUBLE,
238
            SchemaInterface::TYPE_DECIMAL,
239
            SchemaInterface::TYPE_MONEY,
240
            SchemaInterface::TYPE_DATETIME,
241
            SchemaInterface::TYPE_TIMESTAMP,
242
            SchemaInterface::TYPE_TIME,
243
            SchemaInterface::TYPE_DATE,
244
            SchemaInterface::TYPE_JSON => true,
245
            default => false,
246
        };
247
    }
248
}
249