Passed
Pull Request — 2.x (#116)
by Maxim
17:48
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 bigPrimary()
34
 * @method $this|AbstractColumn boolean()
35
 * @method $this|AbstractColumn integer()
36
 * @method $this|AbstractColumn tinyInteger()
37
 * @method $this|AbstractColumn smallInteger()
38
 * @method $this|AbstractColumn bigInteger()
39
 * @method $this|AbstractColumn text()
40
 * @method $this|AbstractColumn tinyText()
41
 * @method $this|AbstractColumn longText()
42
 * @method $this|AbstractColumn double()
43
 * @method $this|AbstractColumn float()
44
 * @method $this|AbstractColumn date()
45
 * @method $this|AbstractColumn time()
46
 * @method $this|AbstractColumn timestamp()
47
 * @method $this|AbstractColumn binary()
48
 * @method $this|AbstractColumn tinyBinary()
49
 * @method $this|AbstractColumn longBinary()
50
 * @method $this|AbstractColumn json()
51
 * @method $this|AbstractColumn uuid()
52
 */
53
abstract class AbstractColumn implements ColumnInterface, ElementInterface
54
{
55
    use ColumnAttributesTrait;
56
    use ElementTrait;
57
58
    /**
59
     * Default timestamp expression (driver specific).
60
     */
61
    public const DATETIME_NOW = 'CURRENT_TIMESTAMP';
62
63
    /**
64
     * Value to be excluded from comparison.
65
     */
66
    public const EXCLUDE_FROM_COMPARE = ['timezone', 'userType', 'attributes'];
67
68
    /**
69
     * Normalization for time and dates.
70
     */
71
    public const DATE_FORMAT = 'Y-m-d';
72
    public const TIME_FORMAT = 'H:i:s';
73
    public const DATETIME_PRECISION = 6;
74
75
    /**
76
     * Mapping between abstract type and internal database type with it's options. Multiple abstract
77
     * types can map into one database type, this implementation allows us to equalize two columns
78
     * if they have different abstract types but same database one. Must be declared by DBMS
79
     * specific implementation.
80
     *
81
     * Example:
82
     * integer => array('type' => 'int', 'size' => 1),
83
     * boolean => array('type' => 'tinyint', 'size' => 1)
84
     *
85
     * @internal
86
     */
87
    protected array $mapping = [
88
        //Primary sequences
89
        'primary'     => null,
90
        'bigPrimary'  => null,
91
92
        //Enum type (mapped via method)
93
        'enum'        => null,
94
95
        //Logical types
96
        'boolean'     => null,
97
98
        //Integer types (size can always be changed with size method), longInteger has method alias
99
        //bigInteger
100
        'integer'     => null,
101
        'tinyInteger' => null,
102
        'smallInteger'=> null,
103
        'bigInteger'  => null,
104
105
        //String with specified length (mapped via method)
106
        'string'      => null,
107
108
        //Generic types
109
        'text'        => null,
110
        'tinyText'    => null,
111
        'longText'    => null,
112
113
        //Real types
114
        'double'      => null,
115
        'float'       => null,
116
117
        //Decimal type (mapped via method)
118
        'decimal'     => null,
119
120
        //Date and Time types
121
        'datetime'    => null,
122
        'date'        => null,
123
        'time'        => null,
124
        'timestamp'   => null,
125
126
        //Binary types
127
        'binary'      => null,
128
        'tinyBinary'  => null,
129
        'longBinary'  => null,
130
131
        //Additional types
132
        'json'        => null,
133
    ];
134
135
    /**
136
     * Reverse mapping is responsible for generating abstract type based on database type and it's
137
     * options. Multiple database types can be mapped into one abstract type.
138
     *
139
     * @internal
140
     */
141
    protected array $reverseMapping = [
142
        'primary'     => [],
143
        'bigPrimary'  => [],
144
        'enum'        => [],
145
        'boolean'     => [],
146
        'integer'     => [],
147
        'tinyInteger' => [],
148
        'smallInteger'=> [],
149
        'bigInteger'  => [],
150
        'string'      => [],
151
        'text'        => [],
152
        'tinyText'    => [],
153
        'longText'    => [],
154
        'double'      => [],
155
        'float'       => [],
156
        'decimal'     => [],
157
        'datetime'    => [],
158
        'date'        => [],
159
        'time'        => [],
160
        'timestamp'   => [],
161
        'binary'      => [],
162
        'tinyBinary'  => [],
163
        'longBinary'  => [],
164
        'json'        => [],
165
    ];
166
167
    /**
168
     * User defined type. Only until actual mapping.
169
     */
170
    protected ?string $userType = null;
171
172
    /**
173
     * DBMS specific column type.
174
     */
175
    protected string $type = '';
176
177
    protected ?\DateTimeZone $timezone = null;
178
179
    /**
180
     * Indicates that column can contain null values.
181
     */
182
    #[ColumnAttribute]
183
    protected bool $nullable = true;
184
185
    /**
186
     * Default column value, may not be applied to some datatypes (for example to primary keys),
187
     * should follow type size and other options.
188
     */
189
    #[ColumnAttribute]
190
    protected mixed $defaultValue = null;
191
192
    /**
193
     * Column type size, can have different meanings for different datatypes.
194
     */
195
    #[ColumnAttribute]
196
    protected int $size = 0;
197
198
    /**
199
     * Precision of column, applied only for "decimal" type.
200
     */
201
    #[ColumnAttribute(['decimal'])]
202
    protected int $precision = 0;
203
204
    /**
205
     * Scale of column, applied only for "decimal" type.
206
     */
207
    #[ColumnAttribute(['decimal'])]
208
    protected int $scale = 0;
209
210
    /**
211
     * Indicates that column is readonly. Readonly columns are excluded from comparison.
212
     */
213
    #[ColumnAttribute]
214
    protected bool $readOnly = false;
215
216
    /**
217
     * List of allowed enum values.
218
     */
219
    protected array $enumValues = [];
220
221
    /**
222
     * Abstract type aliases (for consistency).
223
     */
224
    protected array $aliases = [
225
        'int'            => 'integer',
226
        'smallint'       => 'smallInteger',
227
        'bigint'         => 'bigInteger',
228
        'incremental'    => 'primary',
229
        'bigIncremental' => 'bigPrimary',
230
        'bool'           => 'boolean',
231
        'blob'           => 'binary',
232 1950
    ];
233
234
    /**
235
     * Association list between abstract types and native PHP types. Every non listed type will be
236
     * converted into string.
237 1950
     *
238 1950
     * @internal
239
     */
240
    private array $phpMapping = [
241
        self::INT   => ['primary', 'bigPrimary', 'integer', 'tinyInteger', 'smallInteger', 'bigInteger'],
242
        self::BOOL  => ['boolean'],
243
        self::FLOAT => ['double', 'float', 'decimal'],
244
    ];
245 1750
246
    /**
247 1750
     * @psalm-param non-empty-string $table
248
     * @psalm-param non-empty-string $name
249
     */
250 856
    public function __construct(
251
        protected string $table,
252 856
        protected string $name,
253
        \DateTimeZone $timezone = null
254
    ) {
255
        $this->timezone = $timezone ?? new \DateTimeZone(date_default_timezone_get());
256
    }
257
258 24
    /**
259
     * Shortcut for AbstractColumn->type() method.
260 24
     *
261 24
     * @psalm-param non-empty-string $name
262
     */
263 24
    public function __call(string $name, array $arguments = []): self
264 24
    {
265 24
        if (isset($this->aliases[$name]) || isset($this->mapping[$name])) {
266
            $this->type($name);
267
        }
268
269 24
        // The type must be set before the attributes are filled.
270 8
        !empty($this->type) or throw new SchemaException('Undefined abstract/virtual type');
271
272
        if (\count($arguments) === 1 && \key($arguments) === 0) {
273 24
            if (\array_key_exists($name, $this->getAttributesMap())) {
274 24
                $this->fillAttributes([$name => $arguments[0]]);
275
                return $this;
276
            }
277 24
        }
278 8
279
        $this->fillAttributes($arguments);
280
281 24
        return $this;
282
    }
283
284
    public function __toString(): string
285 24
    {
286 16
        return $this->table . '.' . $this->getName();
287 16
    }
288
289
    /**
290 24
     * Simplified way to dump information.
291
     */
292
    public function __debugInfo(): array
293 8
    {
294
        $column = [
295 8
            'name' => $this->name,
296
            'type' => [
297
                'database' => $this->type,
298 852
                'schema'   => $this->getAbstractType(),
299
                'php'      => $this->getType(),
300 852
            ],
301
        ];
302
303 852
        if (!empty($this->size)) {
304
            $column['size'] = $this->size;
305 852
        }
306
307
        if ($this->nullable) {
308 8
            $column['nullable'] = true;
309
        }
310 8
311
        if ($this->defaultValue !== null) {
312
            $column['defaultValue'] = $this->getDefaultValue();
313 1798
        }
314
315 1798
        if (static::isEnum($this)) {
316
            $column['enumValues'] = $this->enumValues;
317
        }
318
319
        if ($this->getAbstractType() === 'decimal') {
320
            $column['precision'] = $this->precision;
321 1798
            $column['scale'] = $this->scale;
322
        }
323 1798
324 1514
        if ($this->attributes !== []) {
325
            $column['attributes'] = $this->attributes;
326
        }
327 1074
328
        return $column;
329 368
    }
330
331
    public function getSize(): int
332 850
    {
333 682
        return $this->size;
334
    }
335
336 698
    public function getPrecision(): int
337 170
    {
338 293
        return $this->precision;
339 205
    }
340 205
341 698
    public function getScale(): int
342
    {
343
        return $this->scale;
344
    }
345
346
    public function isNullable(): bool
347
    {
348 60
        return $this->nullable;
349
    }
350 60
351
    public function hasDefaultValue(): bool
352
    {
353
        return $this->defaultValue !== null;
354
    }
355
356 852
    /**
357
     * @throws DefaultValueException
358 852
     */
359
    public function getDefaultValue(): mixed
360
    {
361 856
        if (!$this->hasDefaultValue()) {
362
            return null;
363 856
        }
364
365
        if ($this->defaultValue instanceof FragmentInterface) {
366
            //Defined as SQL piece
367
            return $this->defaultValue;
368
        }
369 842
370
        if (\in_array($this->getAbstractType(), ['time', 'date', 'datetime', 'timestamp'])) {
371 842
            return $this->formatDatetime($this->getAbstractType(), $this->defaultValue);
372 842
        }
373 842
374 618
        return match ($this->getType()) {
375
            'int' => (int) $this->defaultValue,
376
            'float' => (float) $this->defaultValue,
377
            'bool' => \is_string($this->defaultValue) && strtolower($this->defaultValue) === 'false'
378 754
                ? false : (bool) $this->defaultValue,
379
            default => (string)$this->defaultValue
380
        };
381
    }
382
383
    /**
384
     * Get every associated column constraint names.
385
     */
386
    public function getConstraints(): array
387
    {
388
        return [];
389
    }
390
391
    /**
392
     * Get allowed enum values.
393
     */
394
    public function getEnumValues(): array
395
    {
396 1922
        return $this->enumValues;
397
    }
398 1922
399 1922
    public function getInternalType(): string
400 1922
    {
401 1890
        return $this->type;
402 1862
    }
403
404
    /**
405 1838
     * @psalm-return non-empty-string
406
     */
407
    public function getType(): string
408 1414
    {
409 1360
        $schemaType = $this->getAbstractType();
410
        foreach ($this->phpMapping as $phpType => $candidates) {
411
            if (\in_array($schemaType, $candidates, true)) {
412 1034
                return $phpType;
413 1034
            }
414 1034
        }
415
416
        return self::STRING;
417 1034
    }
418 876
419
    /**
420
     * Returns type defined by the user, only until schema sync. Attention, this value is only preserved during the
421
     * declaration process. Value will become null after the schema fetched from database.
422 688
     *
423
     * @internal
424
     */
425
    public function getDeclaredType(): ?string
426 4
    {
427
        return $this->userType;
428
    }
429
430
    /**
431
     * DBMS specific reverse mapping must map database specific type into limited set of abstract
432
     * types.
433
     */
434
    public function getAbstractType(): string
435
    {
436
        foreach ($this->reverseMapping as $type => $candidates) {
437
            foreach ($candidates as $candidate) {
438
                if (\is_string($candidate)) {
439
                    if (strtolower($candidate) === strtolower($this->type)) {
440
                        return $type;
441
                    }
442 1932
443
                    continue;
444 1932
                }
445
446
                if (strtolower($candidate['type']) !== strtolower($this->type)) {
447
                    continue;
448
                }
449 1932
450
                foreach ($candidate as $option => $required) {
451
                    if ($option === 'type') {
452 1932
                        continue;
453
                    }
454
455 1932
                    if ($this->{$option} !== $required) {
456 1932
                        continue 2;
457
                    }
458
                }
459 1932
460 1656
                return $type;
461
            }
462 1656
        }
463
464
        return 'unknown';
465
    }
466 1442
467 1442
    /**
468
     * Give column new abstract type. DBMS specific implementation must map provided type into one
469
     * of internal database values.
470 1442
     *
471
     * Attention, changing type of existed columns in some databases has a lot of restrictions like
472
     * cross type conversions and etc. Try do not change column type without a reason.
473
     *
474
     * @psalm-param non-empty-string $abstract Abstract or virtual type declared in mapping.
475
     *
476 698
     * @todo Support native database types (simply bypass abstractType)!
477
     */
478 698
    public function type(string $abstract): self
479
    {
480 698
        if (isset($this->aliases[$abstract])) {
481
            //Make recursive
482
            $abstract = $this->aliases[$abstract];
483
        }
484
485
        if (!isset($this->mapping[$abstract])) {
486
            $this->type = $abstract;
487 954
            $this->userType = $abstract;
488
489
            return $this;
490 954
        }
491 578
492
        // Originally specified type.
493
        $this->userType = $abstract;
494 954
495
        // Resetting all values to default state.
496 954
        $this->size = $this->precision = $this->scale = 0;
497
        $this->enumValues = [];
498
499
        // Abstract type points to DBMS specific type
500
        if (\is_string($this->mapping[$abstract])) {
501
            $this->type = $this->mapping[$abstract];
502
503
            return $this;
504
        }
505
506
        // Configuring column properties based on abstractType preferences
507
        foreach ($this->mapping[$abstract] as $property => $value) {
508
            $this->{$property} = $value;
509 304
        }
510
511 304
        return $this;
512 304
    }
513 304
514 304
    /**
515
     * Set column nullable/not nullable.
516
     */
517 304
    public function nullable(bool $nullable = true): self
518
    {
519
        $this->nullable = $nullable;
520
521
        return $this;
522
    }
523
524
    /**
525
     * Change column default value (can be forbidden for some column types).
526
     * Use Database::TIMESTAMP_NOW to use driver specific NOW() function.
527
     */
528
    public function defaultValue(mixed $value): self
529
    {
530
        //Forcing driver specific values
531
        if ($value === self::DATETIME_NOW) {
532
            $value = static::DATETIME_NOW;
533
        }
534 1008
535
        $this->defaultValue = $value;
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 isReadOnly(): bool
688
    {
689 632
        return $this->readOnly;
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