Passed
Pull Request — 2.x (#138)
by Aleksei
18:44
created

AbstractColumn::compare()   C

Complexity

Conditions 13
Paths 14

Size

Total Lines 45
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 13.0245

Importance

Changes 0
Metric Value
cc 13
eloc 23
c 0
b 0
f 0
nc 14
nop 1
dl 0
loc 45
ccs 18
cts 19
cp 0.9474
crap 13.0245
rs 6.6166

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
/**
4
 * This file is part of Cycle ORM package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Database\Schema;
13
14
use Cycle\Database\Schema\Attribute\ColumnAttribute;
15
use Cycle\Database\Schema\Traits\ColumnAttributesTrait;
16
use DateTimeImmutable;
17
use Cycle\Database\ColumnInterface;
18
use Cycle\Database\Driver\DriverInterface;
19
use Cycle\Database\Exception\DefaultValueException;
20
use Cycle\Database\Exception\SchemaException;
21
use Cycle\Database\Injection\Fragment;
22
use Cycle\Database\Injection\FragmentInterface;
23
use Cycle\Database\Query\QueryParameters;
24
use Cycle\Database\Schema\Traits\ElementTrait;
25
26
/**
27
 * Abstract column schema with read (see ColumnInterface) and write abilities. Must be implemented
28
 * by driver to support DBMS specific syntax and creation rules.
29
 *
30
 * Shortcuts for various column types:
31
 *
32
 * @method $this|AbstractColumn primary()
33
 * @method $this|AbstractColumn smallPrimary()
34
 * @method $this|AbstractColumn bigPrimary()
35
 * @method $this|AbstractColumn boolean()
36
 * @method $this|AbstractColumn integer()
37
 * @method $this|AbstractColumn tinyInteger()
38
 * @method $this|AbstractColumn smallInteger()
39
 * @method $this|AbstractColumn bigInteger()
40
 * @method $this|AbstractColumn text()
41
 * @method $this|AbstractColumn tinyText()
42
 * @method $this|AbstractColumn longText()
43
 * @method $this|AbstractColumn double()
44
 * @method $this|AbstractColumn float()
45
 * @method $this|AbstractColumn date()
46
 * @method $this|AbstractColumn time()
47
 * @method $this|AbstractColumn timestamp()
48
 * @method $this|AbstractColumn binary()
49
 * @method $this|AbstractColumn tinyBinary()
50
 * @method $this|AbstractColumn longBinary()
51
 * @method $this|AbstractColumn json()
52
 * @method $this|AbstractColumn uuid()
53
 */
54
abstract class AbstractColumn implements ColumnInterface, ElementInterface
55
{
56
    use ColumnAttributesTrait;
57
    use ElementTrait;
58
59
    /**
60
     * Default timestamp expression (driver specific).
61
     */
62
    public const DATETIME_NOW = 'CURRENT_TIMESTAMP';
63
64
    /**
65
     * Value to be excluded from comparison.
66
     */
67
    public const EXCLUDE_FROM_COMPARE = ['timezone', 'userType', 'attributes'];
68
69
    /**
70
     * Normalization for time and dates.
71
     */
72
    public const DATE_FORMAT = 'Y-m-d';
73
    public const TIME_FORMAT = 'H:i:s';
74
    public const DATETIME_PRECISION = 6;
75
76
    /**
77
     * Mapping between abstract type and internal database type with it's options. Multiple abstract
78
     * types can map into one database type, this implementation allows us to equalize two columns
79
     * if they have different abstract types but same database one. Must be declared by DBMS
80
     * specific implementation.
81
     *
82
     * Example:
83
     * integer => array('type' => 'int', 'size' => 1),
84
     * boolean => array('type' => 'tinyint', 'size' => 1)
85
     *
86
     * @internal
87
     */
88
    protected array $mapping = [
89
        //Primary sequences
90
        'primary'     => null,
91
        'smallPrimary'  => null,
92
        'bigPrimary'  => null,
93
94
        //Enum type (mapped via method)
95
        'enum'        => null,
96
97
        //Logical types
98
        'boolean'     => null,
99
100
        //Integer types (size can always be changed with size method), longInteger has method alias
101
        //bigInteger
102
        'integer'     => null,
103
        'tinyInteger' => null,
104
        'smallInteger'=> null,
105
        'bigInteger'  => null,
106
107
        //String with specified length (mapped via method)
108
        'string'      => null,
109
110
        //Generic types
111
        'text'        => null,
112
        'tinyText'    => null,
113
        'longText'    => null,
114
115
        //Real types
116
        'double'      => null,
117
        'float'       => null,
118
119
        //Decimal type (mapped via method)
120
        'decimal'     => null,
121
122
        //Date and Time types
123
        'datetime'    => null,
124
        'date'        => null,
125
        'time'        => null,
126
        'timestamp'   => null,
127
128
        //Binary types
129
        'binary'      => null,
130
        'tinyBinary'  => null,
131
        'longBinary'  => null,
132
133
        //Additional types
134
        'json'        => null,
135
    ];
136
137
    /**
138
     * Reverse mapping is responsible for generating abstract type based on database type and it's
139
     * options. Multiple database types can be mapped into one abstract type.
140
     *
141
     * @internal
142
     */
143
    protected array $reverseMapping = [
144
        'primary'     => [],
145
        'smallPrimary'  => [],
146
        'bigPrimary'  => [],
147
        'enum'        => [],
148
        'boolean'     => [],
149
        'integer'     => [],
150
        'tinyInteger' => [],
151
        'smallInteger'=> [],
152
        'bigInteger'  => [],
153
        'string'      => [],
154
        'text'        => [],
155
        'tinyText'    => [],
156
        'longText'    => [],
157
        'double'      => [],
158
        'float'       => [],
159
        'decimal'     => [],
160
        'datetime'    => [],
161
        'date'        => [],
162
        'time'        => [],
163
        'timestamp'   => [],
164
        'binary'      => [],
165
        'tinyBinary'  => [],
166
        'longBinary'  => [],
167
        'json'        => [],
168
    ];
169
170
    /**
171
     * User defined type. Only until actual mapping.
172
     */
173
    protected ?string $userType = null;
174
175
    /**
176
     * DBMS specific column type.
177
     */
178
    protected string $type = '';
179
180
    protected ?\DateTimeZone $timezone = null;
181
182
    /**
183
     * Indicates that column can contain null values.
184
     */
185
    #[ColumnAttribute]
186
    protected bool $nullable = true;
187
188
    /**
189
     * Default column value, may not be applied to some datatypes (for example to primary keys),
190
     * should follow type size and other options.
191
     */
192
    #[ColumnAttribute]
193
    protected mixed $defaultValue = null;
194
195
    /**
196
     * Column type size, can have different meanings for different datatypes.
197
     */
198
    #[ColumnAttribute]
199
    protected int $size = 0;
200
201
    /**
202
     * Precision of column, applied only for "decimal" type.
203
     */
204
    #[ColumnAttribute(['decimal'])]
205
    protected int $precision = 0;
206
207
    /**
208
     * Scale of column, applied only for "decimal" type.
209
     */
210
    #[ColumnAttribute(['decimal'])]
211
    protected int $scale = 0;
212
213
    /**
214
     * List of allowed enum values.
215
     */
216
    protected array $enumValues = [];
217
218
    /**
219
     * Abstract type aliases (for consistency).
220
     */
221
    protected array $aliases = [
222
        'int'            => 'integer',
223
        'smallint'       => 'smallInteger',
224
        'bigint'         => 'bigInteger',
225
        'incremental'    => 'primary',
226
        'smallIncremental' => 'smallPrimary',
227
        'bigIncremental' => 'bigPrimary',
228
        'bool'           => 'boolean',
229
        'blob'           => 'binary',
230
    ];
231
232 1950
    /**
233
     * Association list between abstract types and native PHP types. Every non listed type will be
234
     * converted into string.
235
     *
236
     * @internal
237 1950
     */
238 1950
    private array $phpMapping = [
239
        self::INT   => ['primary', 'smallPrimary', 'bigPrimary', 'integer', 'tinyInteger', 'smallInteger', 'bigInteger'],
240
        self::BOOL  => ['boolean'],
241
        self::FLOAT => ['double', 'float', 'decimal'],
242
    ];
243
244
    /**
245 1750
     * @psalm-param non-empty-string $table
246
     * @psalm-param non-empty-string $name
247 1750
     */
248
    public function __construct(
249
        protected string $table,
250 856
        protected string $name,
251
        \DateTimeZone $timezone = null
252 856
    ) {
253
        $this->timezone = $timezone ?? new \DateTimeZone(date_default_timezone_get());
254
    }
255
256
    /**
257
     * Shortcut for AbstractColumn->type() method.
258 24
     *
259
     * @psalm-param non-empty-string $name
260 24
     */
261 24
    public function __call(string $name, array $arguments = []): self
262
    {
263 24
        if (isset($this->aliases[$name]) || isset($this->mapping[$name])) {
264 24
            $this->type($name);
265 24
        }
266
267
        // The type must be set before the attributes are filled.
268
        !empty($this->type) or throw new SchemaException('Undefined abstract/virtual type');
269 24
270 8
        if (\count($arguments) === 1 && \key($arguments) === 0) {
271
            if (\array_key_exists($name, $this->getAttributesMap())) {
272
                $this->fillAttributes([$name => $arguments[0]]);
273 24
                return $this;
274 24
            }
275
        }
276
277 24
        $this->fillAttributes($arguments);
278 8
279
        return $this;
280
    }
281 24
282
    public function __toString(): string
283
    {
284
        return $this->table . '.' . $this->getName();
285 24
    }
286 16
287 16
    /**
288
     * Simplified way to dump information.
289
     */
290 24
    public function __debugInfo(): array
291
    {
292
        $column = [
293 8
            'name' => $this->name,
294
            'type' => [
295 8
                'database' => $this->type,
296
                'schema'   => $this->getAbstractType(),
297
                'php'      => $this->getType(),
298 852
            ],
299
        ];
300 852
301
        if (!empty($this->size)) {
302
            $column['size'] = $this->size;
303 852
        }
304
305 852
        if ($this->nullable) {
306
            $column['nullable'] = true;
307
        }
308 8
309
        if ($this->defaultValue !== null) {
310 8
            $column['defaultValue'] = $this->getDefaultValue();
311
        }
312
313 1798
        if (static::isEnum($this)) {
314
            $column['enumValues'] = $this->enumValues;
315 1798
        }
316
317
        if ($this->getAbstractType() === 'decimal') {
318
            $column['precision'] = $this->precision;
319
            $column['scale'] = $this->scale;
320
        }
321 1798
322
        if ($this->attributes !== []) {
323 1798
            $column['attributes'] = $this->attributes;
324 1514
        }
325
326
        return $column;
327 1074
    }
328
329 368
    public function getSize(): int
330
    {
331
        return $this->size;
332 850
    }
333 682
334
    public function getPrecision(): int
335
    {
336 698
        return $this->precision;
337 170
    }
338 293
339 205
    public function getScale(): int
340 205
    {
341 698
        return $this->scale;
342
    }
343
344
    public function isNullable(): bool
345
    {
346
        return $this->nullable;
347
    }
348 60
349
    public function hasDefaultValue(): bool
350 60
    {
351
        return $this->defaultValue !== null;
352
    }
353
354
    /**
355
     * @throws DefaultValueException
356 852
     */
357
    public function getDefaultValue(): mixed
358 852
    {
359
        if (!$this->hasDefaultValue()) {
360
            return null;
361 856
        }
362
363 856
        if ($this->defaultValue instanceof FragmentInterface) {
364
            //Defined as SQL piece
365
            return $this->defaultValue;
366
        }
367
368
        if (\in_array($this->getAbstractType(), ['time', 'date', 'datetime', 'timestamp'])) {
369 842
            return $this->formatDatetime($this->getAbstractType(), $this->defaultValue);
370
        }
371 842
372 842
        return match ($this->getType()) {
373 842
            'int' => (int) $this->defaultValue,
374 618
            'float' => (float) $this->defaultValue,
375
            'bool' => \is_string($this->defaultValue) && strtolower($this->defaultValue) === 'false'
376
                ? false : (bool) $this->defaultValue,
377
            default => (string)$this->defaultValue
378 754
        };
379
    }
380
381
    /**
382
     * Get every associated column constraint names.
383
     */
384
    public function getConstraints(): array
385
    {
386
        return [];
387
    }
388
389
    /**
390
     * Get allowed enum values.
391
     */
392
    public function getEnumValues(): array
393
    {
394
        return $this->enumValues;
395
    }
396 1922
397
    public function getInternalType(): string
398 1922
    {
399 1922
        return $this->type;
400 1922
    }
401 1890
402 1862
    /**
403
     * @psalm-return non-empty-string
404
     */
405 1838
    public function getType(): string
406
    {
407
        $schemaType = $this->getAbstractType();
408 1414
        foreach ($this->phpMapping as $phpType => $candidates) {
409 1360
            if (\in_array($schemaType, $candidates, true)) {
410
                return $phpType;
411
            }
412 1034
        }
413 1034
414 1034
        return self::STRING;
415
    }
416
417 1034
    /**
418 876
     * Returns type defined by the user, only until schema sync. Attention, this value is only preserved during the
419
     * declaration process. Value will become null after the schema fetched from database.
420
     *
421
     * @internal
422 688
     */
423
    public function getDeclaredType(): ?string
424
    {
425
        return $this->userType;
426 4
    }
427
428
    /**
429
     * DBMS specific reverse mapping must map database specific type into limited set of abstract
430
     * types.
431
     */
432
    public function getAbstractType(): string
433
    {
434
        foreach ($this->reverseMapping as $type => $candidates) {
435
            foreach ($candidates as $candidate) {
436
                if (\is_string($candidate)) {
437
                    if (strtolower($candidate) === strtolower($this->type)) {
438
                        return $type;
439
                    }
440
441
                    continue;
442 1932
                }
443
444 1932
                if (strtolower($candidate['type']) !== strtolower($this->type)) {
445
                    continue;
446
                }
447
448
                foreach ($candidate as $option => $required) {
449 1932
                    if ($option === 'type') {
450
                        continue;
451
                    }
452 1932
453
                    if ($this->{$option} !== $required) {
454
                        continue 2;
455 1932
                    }
456 1932
                }
457
458
                return $type;
459 1932
            }
460 1656
        }
461
462 1656
        return 'unknown';
463
    }
464
465
    /**
466 1442
     * Give column new abstract type. DBMS specific implementation must map provided type into one
467 1442
     * of internal database values.
468
     *
469
     * Attention, changing type of existed columns in some databases has a lot of restrictions like
470 1442
     * cross type conversions and etc. Try do not change column type without a reason.
471
     *
472
     * @psalm-param non-empty-string $abstract Abstract or virtual type declared in mapping.
473
     *
474
     * @todo Support native database types (simply bypass abstractType)!
475
     */
476 698
    public function type(string $abstract): self
477
    {
478 698
        if (isset($this->aliases[$abstract])) {
479
            //Make recursive
480 698
            $abstract = $this->aliases[$abstract];
481
        }
482
483
        if (!isset($this->mapping[$abstract])) {
484
            $this->type = $abstract;
485
            $this->userType = $abstract;
486
487 954
            return $this;
488
        }
489
490 954
        // Originally specified type.
491 578
        $this->userType = $abstract;
492
493
        // Resetting all values to default state.
494 954
        $this->size = $this->precision = $this->scale = 0;
495
        $this->enumValues = [];
496 954
497
        // Abstract type points to DBMS specific type
498
        if (\is_string($this->mapping[$abstract])) {
499
            $this->type = $this->mapping[$abstract];
500
501
            return $this;
502
        }
503
504
        // Configuring column properties based on abstractType preferences
505
        foreach ($this->mapping[$abstract] as $property => $value) {
506
            $this->{$property} = $value;
507
        }
508
509 304
        return $this;
510
    }
511 304
512 304
    /**
513 304
     * Set column nullable/not nullable.
514 304
     */
515
    public function nullable(bool $nullable = true): self
516
    {
517 304
        $this->nullable = $nullable;
518
519
        return $this;
520
    }
521
522
    /**
523
     * Change column default value (can be forbidden for some column types).
524
     * Use Database::TIMESTAMP_NOW to use driver specific NOW() function.
525
     */
526
    public function defaultValue(mixed $value): self
527
    {
528
        $this->defaultValue = match (true) {
529
            $value === self::DATETIME_NOW => static::DATETIME_NOW,
530
            static::isJson($this) !== false && \is_array($value) => \json_encode(
531
                $value,
532
                \JSON_UNESCAPED_UNICODE|\JSON_THROW_ON_ERROR,
533
            ),
534 1008
            default => $value
535
        };
536 1008
537
        return $this;
538 1008
    }
539
540 1008
    /**
541
     * Set column as enum type and specify set of allowed values. Most of drivers will emulate enums
542 1008
     * using column constraints.
543
     *
544
     * Examples:
545
     * $table->status->enum(['active', 'disabled']);
546
     * $table->status->enum('active', 'disabled');
547
     *
548
     * @param array|string $values Enum values (array or comma separated). String values only.
549
     */
550 64
    public function enum(string|array $values): self
551
    {
552 64
        $this->type('enum');
553
        $this->enumValues = array_map(
554 64
            'strval',
555
            is_array($values) ? $values : func_get_args()
0 ignored issues
show
introduced by
The condition is_array($values) is always true.
Loading history...
556 56
        );
557 56
558
        return $this;
559 56
    }
560
561
    /**
562 1464
     * Set column type as string with limited size. Maximum allowed size is 255 bytes, use "text"
563
     * abstract types for longer strings.
564 1464
     *
565
     * Strings are perfect type to store email addresses as it big enough to store valid address
566 1464
     * and
567
     * can be covered with unique index.
568 458
     *
569 458
     * @link http://stackoverflow.com/questions/386294/what-is-the-maximum-length-of-a-valid-email-address
570
     *
571 1440
     * @param int $size Max string length.
572 42
     *
573 1410
     * @throws SchemaException
574 878
     */
575
    public function string(int $size = 255): self
576
    {
577 1464
        $this->type('string');
578
579 1464
        $size < 0 && throw new SchemaException('Invalid string length value');
580 634
581
        $this->size = $size;
582
583 1464
        return $this;
584
    }
585
586 1894
    public function datetime(int $size = 0, mixed ...$attributes): self
587
    {
588 1894
        $this->type('datetime');
589
        $this->fillAttributes($attributes);
590
591 1894
        ($size < 0 || $size > static::DATETIME_PRECISION) && throw new SchemaException(
592 1838
            \sprintf('Invalid %s precision value.', $this->getAbstractType())
593
        );
594
        $this->size = $size;
595 1598
596 1598
        return $this;
597
    }
598 1598
599 1598
    /**
600 1598
     * Set column type as decimal with specific precision and scale.
601 1598
     *
602
     * @throws SchemaException
603
     */
604 1598
    public function decimal(int $precision, int $scale = 0): self
605
    {
606 1598
        $this->type('decimal');
607 380
608
        empty($precision) && throw new SchemaException('Invalid precision value');
609 1554
610 1554
        $this->precision = $precision;
611
        $this->scale = $scale;
612 16
613
        return $this;
614
    }
615 1598
616
    public function sqlStatement(DriverInterface $driver): string
617
    {
618 1598
        $statement = [$driver->identifier($this->name), $this->type];
0 ignored issues
show
Bug introduced by
The method identifier() does not exist on Cycle\Database\Driver\DriverInterface. It seems like you code against a sub-type of Cycle\Database\Driver\DriverInterface such as Cycle\Database\Driver\Driver. ( Ignorable by Annotation )

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

618
        $statement = [$driver->/** @scrutinizer ignore-call */ identifier($this->name), $this->type];
Loading history...
619 154
620
        if (static::isEnum($this)) {
621
            //Enum specific column options
622
            if (!empty($enumDefinition = $this->quoteEnum($driver))) {
623 1598
                $statement[] = $enumDefinition;
624
            }
625
        } elseif (!empty($this->precision)) {
626
            $statement[] = "({$this->precision}, {$this->scale})";
627
        } elseif (!empty($this->size)) {
628
            $statement[] = "({$this->size})";
629 152
        }
630
631 152
        $statement[] = $this->nullable ? 'NULL' : 'NOT NULL';
632 152
633 152
        if ($this->defaultValue !== null) {
634
            $statement[] = "DEFAULT {$this->quoteDefault($driver)}";
635
        }
636 152
637
        return \implode(' ', $statement);
638
    }
639
640
    public function compare(self $initial): bool
641
    {
642 846
        $normalized = clone $initial;
643
644 846
        // soft compare, todo: improve
645 846
        if ($this == $normalized) {
646
            return true;
647
        }
648
649 846
        $columnVars = get_object_vars($this);
650 578
        $dbColumnVars = get_object_vars($normalized);
651 578
652 578
        $difference = [];
653
        foreach ($columnVars as $name => $value) {
654
            if (\in_array($name, static::EXCLUDE_FROM_COMPARE, true)) {
655
                continue;
656
            }
657 798
658 410
            if ($name === 'type') {
659 293
                // user defined type
660 83
                if (!isset($this->mapping[$this->type]) && $this->type === $this->userType) {
661 798
                    continue;
662
                }
663
            }
664
665
            if ($name === 'defaultValue') {
666
                //Default values has to compared using type-casted value
667
                if ($this->getDefaultValue() != $initial->getDefaultValue()) {
668
                    $difference[] = $name;
669
                } elseif (
670
                    $this->getDefaultValue() !== $initial->getDefaultValue()
671
                    && (!\is_object($this->getDefaultValue()) && !\is_object($initial->getDefaultValue()))
672 682
                ) {
673
                    $difference[] = $name;
674
                }
675
676 682
                continue;
677
            }
678 578
679
            if ($value !== $dbColumnVars[$name]) {
680
                $difference[] = $name;
681 634
            }
682 8
        }
683
684 634
        return empty($difference);
685
    }
686 32
687 32
    public function isReadonlySchema(): bool
688
    {
689 632
        return $this->getAttributes()['readonlySchema'] ?? false;
690
    }
691
692
    /**
693 634
     * Get database specific enum type definition options.
694 32
     */
695 301
    protected function quoteEnum(DriverInterface $driver): string
696
    {
697 634
        $enumValues = [];
698
        foreach ($this->enumValues as $value) {
699
            $enumValues[] = $driver->quote($value);
700
        }
701
702
        return !empty($enumValues) ? '(' . implode(', ', $enumValues) . ')' : '';
703
    }
704
705
    /**
706
     * Must return driver specific default value.
707
     */
708
    protected function quoteDefault(DriverInterface $driver): string
709
    {
710
        $defaultValue = $this->getDefaultValue();
711
        if ($defaultValue === null) {
712
            return 'NULL';
713
        }
714
715
        if ($defaultValue instanceof FragmentInterface) {
716
            return $driver->getQueryCompiler()->compile(
717
                new QueryParameters(),
718
                '',
719
                $defaultValue
720
            );
721
        }
722
723
        return match ($this->getType()) {
724
            'bool' => $defaultValue ? 'TRUE' : 'FALSE',
725
            'float' => sprintf('%F', $defaultValue),
726
            'int' => (string) $defaultValue,
727
            default => $driver->quote($defaultValue)
728
        };
729
    }
730
731
    /**
732
     * Ensure that datetime fields are correctly formatted.
733
     *
734
     * @psalm-param non-empty-string $type
735
     *
736
     * @throws DefaultValueException
737
     */
738
    protected function formatDatetime(
739
        string $type,
740
        string|int|\DateTimeInterface $value
741
    ): \DateTimeInterface|FragmentInterface|string {
742
        if ($value === static::DATETIME_NOW) {
743
            //Dynamic default value
744
            return new Fragment($value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type DateTimeInterface; however, parameter $fragment of Cycle\Database\Injection\Fragment::__construct() 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

744
            return new Fragment(/** @scrutinizer ignore-type */ $value);
Loading history...
745
        }
746
747
        if ($value instanceof \DateTimeInterface) {
748
            $datetime = clone $value;
749
        } else {
750
            if (is_numeric($value)) {
751
                //Presumably timestamp
752
                $datetime = new DateTimeImmutable('now', $this->timezone);
753
                $datetime = $datetime->setTimestamp($value);
0 ignored issues
show
Bug introduced by
It seems like $value can also be of type string; however, parameter $timestamp of DateTimeImmutable::setTimestamp() does only seem to accept integer, 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

753
                $datetime = $datetime->setTimestamp(/** @scrutinizer ignore-type */ $value);
Loading history...
754
            } else {
755
                $datetime = new DateTimeImmutable($value, $this->timezone);
756
            }
757
        }
758
759
        return match ($type) {
760
            'datetime', 'timestamp' => $datetime,
761
            'time' => $datetime->format(static::TIME_FORMAT),
762
            'date' => $datetime->format(static::DATE_FORMAT),
763
            default => $value
764
        };
765
    }
766
767
    protected static function isEnum(self $column): bool
768
    {
769
        return $column->getAbstractType() === 'enum';
770
    }
771
772
    /**
773
     * Checks if the column is JSON or no.
774
     *
775
     * Returns null if it's impossible to explicitly define the JSON type.
776
     */
777
    protected static function isJson(self $column): ?bool
778
    {
779
        return $column->getAbstractType() === 'json';
780
    }
781
}
782