Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
182 / 182
100.00% covered (success)
100.00%
12 / 12
CRAP
100.00% covered (success)
100.00%
1 / 1
Column
100.00% covered (success)
100.00%
182 / 182
100.00% covered (success)
100.00%
12 / 12
110
100.00% covered (success)
100.00%
1 / 1
 getColumnSchema
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getValidDataType
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
6
 getValidMysqlDataType
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
7
 getValidPgsqlDataType
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
15
 getValidSqliteDataType
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
1 / 1
20
 getValidSqlsrvDataType
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
16
 formatColumn
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
6
 formatMysqlColumn
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
10
 formatPgsqlColumn
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
 formatSqliteColumn
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 formatSqlsrvColumn
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
9
 formatCommonParameters
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
9
1<?php
2declare(strict_types=1);
3/**
4 * Pop PHP Framework (https://www.popphp.org/)
5 *
6 * @link       https://github.com/popphp/popphp-framework
7 * @author     Nick Sagona, III <nick@popphp.org>
8 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
9 * @license    https://www.popphp.org/license     New BSD License
10 */
11
12/**
13 * @namespace
14 */
15namespace Pop\Db\Sql\Schema\Formatter;
16
17use Pop\Db\Sql;
18
19/**
20 * Schema column formatter class
21 *
22 * @category   Pop
23 * @package    Pop\Db
24 * @author     Nick Sagona, III <nick@popphp.org>
25 * @copyright  Copyright (c) 2009-2026 Nick Sagona, III
26 * @license    https://www.popphp.org/license     New BSD License
27 * @version    7.0.0
28 */
29class Column extends AbstractFormatter
30{
31
32    /**
33     * Get column schema
34     *
35     * @param  string $dbType
36     * @param  string $name
37     * @param  array  $column
38     * @param  string $table
39     * @throws Exception
40     * @return string
41     */
42    public static function getColumnSchema(string $dbType, string $name, array $column, string $table): string
43    {
44        if (!isset($column['type'])) {
45            throw new Exception('Error: The column type was not set.');
46        }
47
48        $dataType = self::getValidDataType($dbType, $column['type']);
49
50        return self::formatColumn($dbType, $name, $dataType, $column, $table);
51    }
52
53    /**
54     * Get valid column data type
55     *
56     * @param  string $dbType
57     * @param  string $type
58     * @return string
59     */
60    public static function getValidDataType(string $dbType, string $type): string
61    {
62        switch ($dbType) {
63            case Sql::MYSQL:
64                return self::getValidMysqlDataType($type);
65            case Sql::PGSQL:
66                return self::getValidPgsqlDataType($type);
67            case Sql::SQLITE:
68                return self::getValidSqliteDataType($type);
69            case Sql::SQLSRV:
70                return self::getValidSqlsrvDataType($type);
71            default:
72                return $type;
73        }
74    }
75
76    /**
77     * Get valid MySQL data type
78     *
79     * @param  string $type
80     * @return string
81     */
82    public static function getValidMysqlDataType(string $type): string
83    {
84        $type = strtoupper($type);
85
86        switch ($type) {
87            case 'INTEGER':
88                $type = 'INT';
89                break;
90            case 'SERIAL':
91                $type = 'INT';
92                break;
93            case 'BIGSERIAL':
94                $type = 'BIGINT';
95                break;
96            case 'SMALLSERIAL':
97                $type = 'SMALLINT';
98                break;
99            case 'CHARACTER VARYING':
100                $type = 'VARCHAR';
101                break;
102            case 'CHARACTER':
103                $type = 'CHAR';
104                break;
105        }
106
107        return $type;
108    }
109
110    /**
111     * Get valid PostgreSQL data type
112     *
113     * @param  string $type
114     * @return string
115     */
116    public static function getValidPgsqlDataType(string $type): string
117    {
118        $type = strtoupper($type);
119
120        switch ($type) {
121            case 'INT':
122            case 'MEDIUMINT':
123                $type = 'INTEGER';
124                break;
125            case 'TINYINT':
126                $type = 'SMALLINT';
127                break;
128            case 'DOUBLE':
129                $type = 'DOUBLE PRECISION';
130                break;
131            case 'BLOB':
132            case 'TINYBLOB':
133            case 'MEDIUMBLOB':
134            case 'LONGBLOB':
135            case 'TINYTEXT':
136            case 'MEDIUMTEXT':
137            case 'LONGTEXT':
138                $type = 'TEXT';
139                break;
140            case 'BINARY':
141            case 'VARBINARY':
142                $type = 'BYTEA';
143                break;
144            case 'DATETIME':
145                $type = 'TIMESTAMP';
146                break;
147        }
148
149        return $type;
150    }
151
152    /**
153     * Get valid SQLite data type
154     *
155     * @param  string $type
156     * @return string
157     */
158    public static function getValidSqliteDataType(string $type): string
159    {
160        $type = strtoupper($type);
161
162        switch ($type) {
163            case 'INT':
164            case 'SMALLINT':
165            case 'TINYINT':
166            case 'MEDIUMINT':
167            case 'BIGINT':
168            case 'SERIAL':
169            case 'BIGSERIAL':
170            case 'SMALLSERIAL':
171                $type = 'INTEGER';
172                break;
173            case 'FLOAT':
174            case 'DOUBLE':
175            case 'DOUBLE PRECISION':
176                $type = 'REAL';
177                break;
178            case 'DECIMAL':
179                $type = 'NUMERIC';
180                break;
181            case 'TINYBLOB':
182            case 'MEDIUMBLOB':
183            case 'LONGBLOB':
184                $type = 'BLOB';
185                break;
186            case 'TINYTEXT':
187            case 'MEDIUMTEXT':
188            case 'LONGTEXT':
189                $type = 'TEXT';
190                break;
191            case 'TIMESTAMP':
192                $type = 'DATETIME';
193                break;
194        }
195
196        return $type;
197    }
198
199    /**
200     * Get valid SQL Server data type
201     *
202     * @param  string $type
203     * @return string
204     */
205    public static function getValidSqlsrvDataType(string $type): string
206    {
207        $type = strtoupper($type);
208
209        switch ($type) {
210            case 'INTEGER':
211            case 'MEDIUMINT':
212            case 'SERIAL':
213                $type = 'INT';
214                break;
215            case 'BIGSERIAL':
216                $type = 'BIGINT';
217                break;
218            case 'SMALLSERIAL':
219                $type = 'SMALLINT';
220                break;
221            case 'DOUBLE':
222            case 'DOUBLE PRECISION':
223                $type = 'REAL';
224                break;
225            case 'BLOB':
226            case 'TINYBLOB':
227            case 'MEDIUMBLOB':
228            case 'LONGBLOB':
229            case 'TINYTEXT':
230            case 'MEDIUMTEXT':
231            case 'LONGTEXT':
232                $type = 'TEXT';
233                break;
234            case 'TIMESTAMP':
235                $type = 'DATETIME';
236                break;
237        }
238
239        return $type;
240    }
241
242    /**
243     * Format column
244     *
245     * @param  string $dbType
246     * @param  string $name
247     * @param  string $dataType
248     * @param  array  $column
249     * @param  string $table
250     * @throws Exception
251     * @return string
252     */
253    public static function formatColumn(string $dbType, string $name, string $dataType, array $column, string $table): string
254    {
255        switch ($dbType) {
256            case Sql::MYSQL:
257                return self::formatMysqlColumn($name, $dataType, $column);
258            case Sql::PGSQL:
259                return self::formatPgsqlColumn($name, $dataType, $column, $table);
260            case Sql::SQLITE:
261                return self::formatSqliteColumn($name, $dataType, $column);
262            case Sql::SQLSRV:
263                return self::formatSqlsrvColumn($name, $dataType, $column);
264            default:
265                throw new Exception("Error: The database type '" . $dbType . "' is not supported.");
266        }
267    }
268
269    /**
270     * Format MySQL column
271     *
272     * @param  string $name
273     * @param  string $dataType
274     * @param  array  $column
275     * @return string
276     */
277    public static function formatMysqlColumn(string $name, string $dataType, array $column): string
278    {
279        $columnString = $name . ' ' . $dataType;
280        $sizeAllowed  = ['DECIMAL', 'NUMERIC', 'FLOAT', 'DOUBLE', 'REAL', 'DOUBLE PRECISION'];
281
282        if (!empty($column['size']) &&
283            ((stripos($dataType, 'INT') !== false) || (stripos($dataType, 'CHAR') !== false) ||
284                (stripos($dataType, 'BINARY') !== false) || in_array($dataType, $sizeAllowed))) {
285            $columnString .= '(' . $column['size'];
286            $columnString .= (!empty($column['precision']) && in_array($dataType, $sizeAllowed)) ?
287                ', ' . $column['precision'] . ')' : ')';
288        }
289
290        if ($column['unsigned'] !== false) {
291            $columnString .= ' UNSIGNED';
292        }
293
294        $columnString = self::formatCommonParameters($columnString, $column);
295
296        if ($column['increment'] !== false) {
297            $columnString .= ' AUTO_INCREMENT';
298        }
299
300        return $columnString;
301    }
302
303    /**
304     * Format PostgreSQL column
305     *
306     * @param  string $name
307     * @param  string $dataType
308     * @param  array  $column
309     * @param  string $table
310     * @return string
311     */
312    public static function formatPgsqlColumn(string $name, string $dataType, array $column, string $table): string
313    {
314        $columnString     = $name . ' ' . $dataType;
315        $unquotedName     = self::unquoteId($name);
316        $sizeAllowed      = ['DECIMAL', 'NUMERIC', 'FLOAT', 'REAL'];
317        $precisionAllowed = ['DECIMAL', 'NUMERIC'];
318
319        if (!empty($column['size']) &&
320            ((stripos($dataType, 'CHAR') !== false) || in_array($dataType, $sizeAllowed))) {
321            $columnString .= '(' . $column['size'];
322            $columnString .= (!empty($column['precision']) && in_array($dataType, $precisionAllowed)) ?
323                ', ' . $column['precision'] . ')' : ')';
324        }
325
326        $columnString = self::formatCommonParameters($columnString, $column);
327
328        if ($column['increment'] !== false) {
329            $columnString .= ' DEFAULT nextval(\'' . $table . '_' . $unquotedName . '_seq\')';
330        }
331
332        return $columnString;
333    }
334
335    /**
336     * Format SQLite column
337     *
338     * @param  string $name
339     * @param  string $dataType
340     * @param  array  $column
341     * @return string
342     */
343    public static function formatSqliteColumn(string $name, string $dataType, array $column): string
344    {
345        $columnString = $name . ' ' . $dataType;
346        $columnString = self::formatCommonParameters($columnString, $column);
347
348        if ($column['increment'] !== false) {
349            $columnString .= (($column['primary'] !== false) ? ' PRIMARY KEY' : null) . ' AUTOINCREMENT';
350        }
351
352        return $columnString;
353    }
354
355    /**
356     * Format SQL Server column
357     *
358     * @param  string $name
359     * @param  string $dataType
360     * @param  array  $column
361     * @return string
362     */
363    public static function formatSqlsrvColumn(string $name, string $dataType, array $column): string
364    {
365        $columnString = $name . ' ' . $dataType;
366        $sizeAllowed      = ['DECIMAL', 'NUMERIC', 'FLOAT', 'REAL'];
367        $precisionAllowed = ['DECIMAL', 'NUMERIC'];
368
369        if (!empty($column['size']) &&
370            ((stripos($dataType, 'CHAR') !== false) || (stripos($dataType, 'BINARY') !== false) ||
371              in_array($dataType, $sizeAllowed))) {
372            $columnString .= '(' . $column['size'];
373            $columnString .= (!empty($column['precision']) && in_array($dataType, $precisionAllowed)) ?
374                ', ' . $column['precision'] . ')' : ')';
375        }
376
377        $columnString = self::formatCommonParameters($columnString, $column);
378
379        if ($column['increment'] !== false) {
380            $columnString .= (($column['primary'] !== false) ? ' PRIMARY KEY' : null) .
381                ' IDENTITY(' . (int)$column['increment'] . ', 1)';
382        }
383
384        return $columnString;
385    }
386
387    /**
388     * Format common column parameters
389     *
390     * @param  string $columnString
391     * @param  array  $column
392     * @return string
393     */
394    public static function formatCommonParameters(string $columnString, array $column): string
395    {
396        if (count($column['attributes']) > 0) {
397            $columnString .= ' ' . implode(' ', $column['attributes']);
398        }
399
400        if (($column['nullable'] === false) || (strtoupper((string)$column['default']) == 'NOT NULL')) {
401            $columnString .= ' NOT NULL';
402        }
403
404        if (($column['default'] === null) && ($column['nullable'] === true)) {
405            $columnString .= ' DEFAULT NULL';
406        } else if (strtoupper((string)$column['default']) == 'NULL') {
407            $columnString .= ' DEFAULT NULL';
408        } else if ($column['default'] !== null) {
409            $columnString .= " DEFAULT " . ((!Sql::isSupportedFunction($column['default'])) ?
410                    "'" . $column['default'] . "'" : $column['default']);
411        }
412
413        return $columnString;
414    }
415
416}