Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit bbb4d38

Browse files
committedJan 6, 2025·
Merge branch '4.3.x' into 5.0.x
* 4.3.x: Enable platformOptions to be considered for ColumnDiff (PostgreSQL jsonb support) (doctrine#6693) Ignore new PHPStan errors Cleanup obsolete PHPStan ignore rules (doctrine#6697) Add a `NumberType` that maps to the `BcMath\Number` value object (doctrine#6686) PHPStan 2.1.1 (doctrine#6690) PHPUnit 10.5.39 (doctrine#6692) PHPUnit 9.6.22 (doctrine#6691) Bump doctrine/.github from 6.0.0 to 7.1.0 (doctrine#6649) Bump doctrine/.github from 5.3.0 to 6.0.0 (doctrine#6642)
2 parents 22f86e6 + 13aaf2a commit bbb4d38

22 files changed

+652
-267
lines changed
 

‎.github/workflows/coding-standards.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ on:
2525
jobs:
2626
coding-standards:
2727
name: "Coding Standards"
28-
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.3.0"
28+
uses: "doctrine/.github/.github/workflows/coding-standards.yml@7.1.0"
29+
with:
30+
composer-options: "--ignore-platform-req=php+"

‎.github/workflows/documentation.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ on:
1717
jobs:
1818
documentation:
1919
name: "Documentation"
20-
uses: "doctrine/.github/.github/workflows/documentation.yml@5.3.0"
20+
uses: "doctrine/.github/.github/workflows/documentation.yml@7.1.0"

‎.github/workflows/static-analysis.yml

+6-2
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
strategy:
3535
matrix:
3636
php-version:
37-
- "8.3"
37+
- "8.4"
3838

3939
steps:
4040
- name: "Checkout code"
@@ -49,6 +49,8 @@ jobs:
4949

5050
- name: "Install dependencies with Composer"
5151
uses: "ramsey/composer-install@v3"
52+
with:
53+
composer-options: "--ignore-platform-req=php+"
5254

5355
- name: "Run a static analysis with phpstan/phpstan"
5456
run: "vendor/bin/phpstan --error-format=checkstyle | cs2pr"
@@ -60,7 +62,7 @@ jobs:
6062
strategy:
6163
matrix:
6264
php-version:
63-
- "8.3"
65+
- "8.4"
6466

6567
steps:
6668
- name: Checkout code
@@ -75,6 +77,8 @@ jobs:
7577

7678
- name: Install dependencies with Composer
7779
uses: ramsey/composer-install@v3
80+
with:
81+
composer-options: "--ignore-platform-req=php+"
7882

7983
- name: Run static analysis with Vimeo Psalm
8084
run: vendor/bin/psalm --shepherd

‎composer.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@
4040
"doctrine/coding-standard": "12.0.0",
4141
"fig/log-test": "^1",
4242
"jetbrains/phpstorm-stubs": "2023.2",
43-
"phpstan/phpstan": "1.12.6",
44-
"phpstan/phpstan-phpunit": "1.4.0",
45-
"phpstan/phpstan-strict-rules": "^1.6",
46-
"phpunit/phpunit": "10.5.30",
43+
"phpstan/phpstan": "2.1.1",
44+
"phpstan/phpstan-phpunit": "2.0.3",
45+
"phpstan/phpstan-strict-rules": "^2",
46+
"phpunit/phpunit": "10.5.39",
4747
"psalm/plugin-phpunit": "0.19.0",
4848
"slevomat/coding-standard": "8.13.1",
4949
"squizlabs/php_codesniffer": "3.10.2",

‎docs/en/reference/schema-representation.rst

+3-3
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ The following options are considered to be fully portable across all database pl
118118
in the platform.
119119
- **fixed** (boolean): Whether a ``string`` or ``binary`` Doctrine type column has
120120
a fixed length. Defaults to ``false``.
121-
- **precision** (integer): The precision of a Doctrine ``decimal`` or ``float`` type
122-
column that determines the overall maximum number of digits to be stored (including scale).
121+
- **precision** (integer): The precision of a Doctrine ``decimal``, ``number`` or ``float``
122+
type column that determines the overall maximum number of digits to be stored (including scale).
123123
Defaults to ``10``.
124124
- **scale** (integer): The exact number of decimal digits to be stored in a Doctrine
125-
``decimal`` or ``float`` type column. Defaults to ``0``.
125+
``decimal``, ``number`` or ``float`` type column. Defaults to ``0``.
126126
- **customSchemaOptions** (array): Additional options for the column that are
127127
supported by all vendors:
128128

‎docs/en/reference/types.rst

+233-216
Large diffs are not rendered by default.

‎phpstan-baseline.neon

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
parameters:
2+
ignoreErrors:
3+
-
4+
message: '#^Method Doctrine\\DBAL\\Driver\\IBMDB2\\Connection\:\:exec\(\) never returns numeric\-string so it can be removed from the return type\.$#'
5+
identifier: return.unusedType
6+
count: 1
7+
path: src/Driver/IBMDB2/Connection.php
8+
9+
-
10+
message: '#^Method Doctrine\\DBAL\\Driver\\OCI8\\Connection\:\:exec\(\) never returns numeric\-string so it can be removed from the return type\.$#'
11+
identifier: return.unusedType
12+
count: 1
13+
path: src/Driver/OCI8/Connection.php
14+
15+
-
16+
message: '#^Method Doctrine\\DBAL\\Driver\\OCI8\\Result\:\:fetchAllAssociative\(\) should return list\<array\<string, mixed\>\> but returns array\<mixed\>\.$#'
17+
identifier: return.type
18+
count: 1
19+
path: src/Driver/OCI8/Result.php
20+
21+
-
22+
message: '#^Method Doctrine\\DBAL\\Driver\\OCI8\\Result\:\:fetchAllNumeric\(\) should return list\<list\<mixed\>\> but returns array\<mixed\>\.$#'
23+
identifier: return.type
24+
count: 1
25+
path: src/Driver/OCI8/Result.php
26+
27+
-
28+
message: '#^Method Doctrine\\DBAL\\Driver\\PDO\\Result\:\:fetchAll\(\) should return list\<mixed\> but returns array\.$#'
29+
identifier: return.type
30+
count: 1
31+
path: src/Driver/PDO/Result.php
32+
33+
-
34+
message: '#^Method Doctrine\\DBAL\\Driver\\PgSQL\\Result\:\:fetchAllAssociative\(\) should return list\<array\<string, mixed\>\> but returns array\<int, array\<string, mixed\>\>\.$#'
35+
identifier: return.type
36+
count: 1
37+
path: src/Driver/PgSQL/Result.php
38+
39+
-
40+
message: '#^Method Doctrine\\DBAL\\Driver\\PgSQL\\Result\:\:fetchAllNumeric\(\) should return list\<list\<mixed\>\> but returns array\<int, list\<mixed\>\>\.$#'
41+
identifier: return.type
42+
count: 1
43+
path: src/Driver/PgSQL/Result.php
44+
45+
-
46+
message: '#^Method Doctrine\\DBAL\\Driver\\PgSQL\\Result\:\:fetchFirstColumn\(\) should return list\<mixed\> but returns array\<int, bool\|float\|int\|string\|null\>\.$#'
47+
identifier: return.type
48+
count: 1
49+
path: src/Driver/PgSQL/Result.php
50+
51+
-
52+
message: '#^Method Doctrine\\DBAL\\Driver\\SQLite3\\Result\:\:fetchNumeric\(\) should return list\<mixed\>\|false but returns array\|false\.$#'
53+
identifier: return.type
54+
count: 1
55+
path: src/Driver/SQLite3/Result.php
56+
57+
-
58+
message: '#^Template type T is declared as covariant, but occurs in invariant position in property Doctrine\\DBAL\\Schema\\AbstractSchemaManager\:\:\$platform\.$#'
59+
identifier: generics.variance
60+
count: 1
61+
path: src/Schema/AbstractSchemaManager.php
62+
63+
-
64+
message: '#^Loose comparison via "\!\=" is not allowed\.$#'
65+
identifier: notEqual.notAllowed
66+
count: 1
67+
path: src/Schema/ColumnDiff.php
68+
69+
-
70+
message: '#^Method Doctrine\\DBAL\\Schema\\SQLiteSchemaManager\:\:addDetailsToTableForeignKeyColumns\(\) should return list\<array\<string, mixed\>\> but returns array\<int\<0, max\>, array\<string, mixed\>\>\.$#'
71+
identifier: return.type
72+
count: 1
73+
path: src/Schema/SQLiteSchemaManager.php
74+
75+
-
76+
message: '#^Offset string might not exist on array\{application_name\?\: string, charset\?\: string, dbname\?\: string, defaultTableOptions\?\: array\<string, mixed\>, driver\?\: ''ibm_db2''\|''mysqli''\|''oci8''\|''pdo_mysql''\|''pdo_oci''\|''pdo_pgsql''\|''pdo_sqlite''\|''pdo_sqlsrv''\|''pgsql''\|''sqlite3''\|''sqlsrv'', driverClass\?\: class\-string\<Doctrine\\DBAL\\Driver\>, driverOptions\?\: array\<mixed\>, host\?\: string, \.\.\.\}\.$#'
77+
identifier: offsetAccess.notFound
78+
count: 1
79+
path: tests/DriverManagerTest.php
80+
81+
-
82+
message: '#^Call to new Doctrine\\DBAL\\Driver\\PgSQL\\Result\(\) on a separate line has no effect\.$#'
83+
identifier: new.resultUnused
84+
count: 1
85+
path: tests/Functional/Driver/PgSQL/ResultTest.php
86+
87+
-
88+
message: '#^Call to function array_filter\(\) requires parameter \#2 to be passed to avoid loose comparison semantics\.$#'
89+
identifier: arrayFilter.strict
90+
count: 1
91+
path: tests/Functional/Schema/MySQL/JsonCollationTest.php

‎phpstan.neon.dist

+13-31
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
parameters:
22
level: 8
3-
phpVersion: 80300
3+
phpVersion: 80402
44
paths:
55
- src
66
- static-analysis
@@ -67,12 +67,6 @@ parameters:
6767
message: '~^Parameter #1 \$params of method Doctrine\\DBAL\\Driver\:\:connect\(\) expects array~'
6868
path: tests/Driver/PDO/*/DriverTest.php
6969

70-
# DriverManagerTest::testDatabaseUrl() should be refactored as it's too dynamic.
71-
-
72-
message: '~^Offset string does not exist on array{.+}\.$~'
73-
paths:
74-
- tests/DriverManagerTest.php
75-
7670
# There is no way to make this assertion in the code,
7771
# and the API doesn't support parametrization of returned column types.
7872
-
@@ -85,37 +79,25 @@ parameters:
8579

8680
# PHPStan does not understand the array shapes returned by pg_fetch_*() methods.
8781
- '~^Parameter #1 \$row of method Doctrine\\DBAL\\Driver\\PgSQL\\Result\:\:mapAssociativeRow\(\) expects array<string, string\|null>, array<int\|string, string\|null> given\.$~'
88-
- '~^Parameter #1 \$row of method Doctrine\\DBAL\\Driver\\PgSQL\\Result\:\:mapNumericRow\(\) expects array<int, string\|null>, array<int\|string, string\|null> given\.$~'
89-
90-
# https://github.com/phpstan/phpstan-src/pull/2224
91-
-
92-
message: '~^Method Doctrine\\DBAL\\Driver\\Mysqli\\Connection\:\:exec\(\) should return int\|numeric\-string but returns int\|string\.$~'
93-
count: 1
94-
path: src/Driver/Mysqli/Connection.php
95-
96-
# https://github.com/phpstan/phpstan/issues/9429
97-
-
98-
message: '~^Strict comparison using === between int<0, max> and false will always evaluate to false.$~'
99-
paths:
100-
- src/Driver/IBMDB2/Connection.php
101-
- src/Driver/IBMDB2/Result.php
102-
103-
# Ignore the possible false return value of db2_num_rows().
104-
- '~^Method Doctrine\\DBAL\\Driver\\IBMDB2\\Connection\:\:exec\(\) should return int but returns int<0, max>\|false\.$~'
105-
- '~^Method Doctrine\\DBAL\\Driver\\IBMDB2\\Result\:\:rowCount\(\) should return int but returns int<0, max>\|false\.$~'
82+
- '~^Parameter #1 \$row of method Doctrine\\DBAL\\Driver\\PgSQL\\Result\:\:mapNumericRow\(\) expects list<string\|null>, .* given\.$~'
10683

10784
# Required for Psalm compatibility
10885
- '~^Property Doctrine\\DBAL\\Tests\\Types\\BaseDateTypeTestCase\:\:\$currentTimezone \(non-empty-string\) does not accept string\.$~'
10986

110-
# This is a rather complicated closure setup. We understand this, so PHPStan doesn't have to.
87+
# The @throws annotations are part of a contract. Even if the default implementation doen't throw those
88+
# exceptions, the child implementations might do so.
11189
-
112-
message: '#^Parameter \#2 \$callback of function array_reduce expects callable\(\(callable&TIn\)\|Closure\(mixed \$value\)\: mixed\|null, callable\(T\)\: T\)\: \(\(callable&TIn\)\|Closure\(mixed \$value\)\: mixed\|null\), Closure\(callable\|null, callable\)\: \(callable\(T\)\: T\) given\.$#'
113-
path: src/Portability/Converter.php
90+
identifier: throws.unusedType
91+
path: src/Types/Type.php
92+
93+
# We don't narrow the return type of lastInsertId() yet.
94+
- '/^Method Doctrine\\DBAL\\Driver\\.+\\Connection\:\:lastInsertId\(\) never returns (int|false) so it can be removed from the return type\.$/'
11495

115-
# PHPStan does not know the new PDO classes yet.
116-
- '~^Class Pdo\\\w+ not found\.$~'
117-
- '~^Call to an undefined static method PDO\:\:connect\(\)\.$~'
96+
# TODO: Review these errors and fix them.
97+
- identifier: phpunit.assertEquals
11898
includes:
99+
- phpstan-baseline.neon
100+
- phar://phpstan.phar/conf/bleedingEdge.neon
119101
- vendor/phpstan/phpstan-phpunit/extension.neon
120102
- vendor/phpstan/phpstan-phpunit/rules.neon
121103
- vendor/phpstan/phpstan-strict-rules/rules.neon

‎src/Platforms/PostgreSQLPlatform.php

+1
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ public function getAlterTableSQL(TableDiff $diff): array
251251
|| $columnDiff->hasScaleChanged()
252252
|| $columnDiff->hasFixedChanged()
253253
|| $columnDiff->hasLengthChanged()
254+
|| $columnDiff->hasPlatformOptionsChanged()
254255
) {
255256
$type = $newColumn->getType();
256257

‎src/SQL/Builder/CreateSchemaObjectsSQLBuilder.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public function buildSQL(Schema $schema): array
2828
}
2929

3030
/**
31-
* @param list<string> $namespaces
31+
* @param string[] $namespaces
3232
*
3333
* @return list<string>
3434
*/
@@ -46,7 +46,7 @@ private function buildNamespaceStatements(array $namespaces): array
4646
}
4747

4848
/**
49-
* @param list<Table> $tables
49+
* @param Table[] $tables
5050
*
5151
* @return list<string>
5252
*/
@@ -56,7 +56,7 @@ private function buildTableStatements(array $tables): array
5656
}
5757

5858
/**
59-
* @param list<Sequence> $sequences
59+
* @param Sequence[] $sequences
6060
*
6161
* @return list<string>
6262
*/

‎src/Schema/ColumnDiff.php

+8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ public function countChangedProperties(): int
2828
+ (int) $this->hasNotNullChanged()
2929
+ (int) $this->hasNameChanged()
3030
+ (int) $this->hasTypeChanged()
31+
+ (int) $this->hasPlatformOptionsChanged()
3132
+ (int) $this->hasCommentChanged();
3233
}
3334

@@ -124,6 +125,13 @@ public function hasCommentChanged(): bool
124125
});
125126
}
126127

128+
public function hasPlatformOptionsChanged(): bool
129+
{
130+
return $this->hasPropertyChanged(static function (Column $column): array {
131+
return $column->getPlatformOptions();
132+
});
133+
}
134+
127135
private function hasPropertyChanged(callable $property): bool
128136
{
129137
return $property($this->newColumn) !== $property($this->oldColumn);

‎src/Types/NumberType.php

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Types;
6+
7+
use BcMath\Number;
8+
use Doctrine\DBAL\Platforms\AbstractPlatform;
9+
use Doctrine\DBAL\Types\Exception\InvalidType;
10+
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
11+
use TypeError;
12+
use ValueError;
13+
14+
use function is_float;
15+
16+
final class NumberType extends Type
17+
{
18+
/** {@inheritDoc} */
19+
public function getSQLDeclaration(array $column, AbstractPlatform $platform): string
20+
{
21+
return $platform->getDecimalTypeDeclarationSQL($column);
22+
}
23+
24+
public function convertToDatabaseValue(mixed $value, AbstractPlatform $platform): ?string
25+
{
26+
if ($value === null) {
27+
return null;
28+
}
29+
30+
if (! $value instanceof Number) {
31+
throw InvalidType::new($value, static::class, ['null', Number::class]);
32+
}
33+
34+
return (string) $value;
35+
}
36+
37+
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): ?Number
38+
{
39+
if ($value === null) {
40+
return null;
41+
}
42+
43+
// SQLite might return a decimal as float.
44+
if (is_float($value)) {
45+
$value = (string) $value;
46+
}
47+
48+
try {
49+
return new Number($value);
50+
} catch (TypeError | ValueError $e) {
51+
throw ValueNotConvertible::new($value, static::class, previous: $e);
52+
}
53+
}
54+
}

‎src/Types/Type.php

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ abstract class Type
3434
Types::DATETIMETZ_MUTABLE => DateTimeTzType::class,
3535
Types::DATETIMETZ_IMMUTABLE => DateTimeTzImmutableType::class,
3636
Types::DECIMAL => DecimalType::class,
37+
Types::NUMBER => NumberType::class,
3738
Types::ENUM => EnumType::class,
3839
Types::FLOAT => FloatType::class,
3940
Types::GUID => GuidType::class,

‎src/Types/Types.php

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ final class Types
2222
public const DATETIMETZ_MUTABLE = 'datetimetz';
2323
public const DATETIMETZ_IMMUTABLE = 'datetimetz_immutable';
2424
public const DECIMAL = 'decimal';
25+
public const NUMBER = 'number';
2526
public const FLOAT = 'float';
2627
public const ENUM = 'enum';
2728
public const GUID = 'guid';

‎tests/Functional/Platform/AlterDecimalColumnTest.php

+10-6
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@
99
use Doctrine\DBAL\Types\Types;
1010
use PHPUnit\Framework\Attributes\DataProvider;
1111

12+
use function sprintf;
13+
1214
class AlterDecimalColumnTest extends FunctionalTestCase
1315
{
1416
#[DataProvider('scaleAndPrecisionProvider')]
15-
public function testAlterPrecisionAndScale(int $newPrecision, int $newScale): void
17+
public function testAlterPrecisionAndScale(int $newPrecision, int $newScale, string $type): void
1618
{
1719
$table = new Table('decimal_table');
18-
$column = $table->addColumn('val', Types::DECIMAL, ['precision' => 16, 'scale' => 6]);
20+
$column = $table->addColumn('val', $type, ['precision' => 16, 'scale' => 6]);
1921

2022
$this->dropAndCreateTable($table);
2123

@@ -36,11 +38,13 @@ public function testAlterPrecisionAndScale(int $newPrecision, int $newScale): vo
3638
self::assertSame($newScale, $column->getScale());
3739
}
3840

39-
/** @return iterable<string,array{int,int}> */
41+
/** @return iterable<string,array{int,int,Types::*}> */
4042
public static function scaleAndPrecisionProvider(): iterable
4143
{
42-
yield 'Precision' => [12, 6];
43-
yield 'Scale' => [16, 8];
44-
yield 'Precision and scale' => [10, 4];
44+
foreach ([Types::DECIMAL, Types::NUMBER] as $type) {
45+
yield sprintf('Precision (%s)', $type) => [12, 6, $type];
46+
yield sprintf('Scale (%s)', $type) => [16, 8, $type];
47+
yield sprintf('Precision and scale (%s)', $type) => [10, 4, $type];
48+
}
4549
}
4650
}

‎tests/Functional/Schema/PostgreSQL/ComparatorTest.php

+19
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,25 @@ public function testCompareBinariesOfDifferentLength(): void
7272
});
7373
}
7474

75+
public function testPlatformOptionsChangedColumnComparison(): void
76+
{
77+
$table = new Table('update_json_to_jsonb_table');
78+
$table->addColumn('test', Types::JSON);
79+
80+
$onlineTable = clone $table;
81+
$table->getColumn('test')
82+
->setPlatformOption('jsonb', true);
83+
84+
$compareResult = $this->comparator->compareTables($onlineTable, $table);
85+
self::assertCount(1, $compareResult->getChangedColumns());
86+
self::assertCount(1, $compareResult->getModifiedColumns());
87+
88+
$changedColumn = $compareResult->getChangedColumns()['test'];
89+
90+
self::assertTrue($changedColumn->hasPlatformOptionsChanged());
91+
self::assertEquals(1, $changedColumn->countChangedProperties());
92+
}
93+
7594
private function testColumnModification(callable $addColumn, callable $modifyColumn): void
7695
{
7796
$table = new Table('comparator_test');

‎tests/Functional/TypeConversionTest.php

+19
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@
44

55
namespace Doctrine\DBAL\Tests\Functional;
66

7+
use BcMath\Number;
78
use DateTime;
89
use Doctrine\DBAL\Schema\Table;
910
use Doctrine\DBAL\Tests\FunctionalTestCase;
1011
use Doctrine\DBAL\Tests\TestUtil;
1112
use Doctrine\DBAL\Types\Type;
1213
use Doctrine\DBAL\Types\Types;
1314
use PHPUnit\Framework\Attributes\DataProvider;
15+
use PHPUnit\Framework\Attributes\RequiresPhp;
16+
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
1417

1518
use function str_repeat;
1619

@@ -38,6 +41,7 @@ protected function setUp(): void
3841
$table->addColumn('test_float', Types::FLOAT, ['notnull' => false]);
3942
$table->addColumn('test_smallfloat', Types::SMALLFLOAT, ['notnull' => false]);
4043
$table->addColumn('test_decimal', Types::DECIMAL, ['notnull' => false, 'scale' => 2, 'precision' => 10]);
44+
$table->addColumn('test_number', Types::NUMBER, ['notnull' => false, 'scale' => 2, 'precision' => 10]);
4145
$table->setPrimaryKey(['id']);
4246

4347
$this->dropAndCreateTable($table);
@@ -154,6 +158,21 @@ public static function toDateTimeProvider(): iterable
154158
];
155159
}
156160

161+
public function testDecimal(): void
162+
{
163+
self::assertSame('13.37', $this->processValue(Types::DECIMAL, '13.37'));
164+
}
165+
166+
#[RequiresPhp('8.4')]
167+
#[RequiresPhpExtension('bcmath')]
168+
public function testNumber(): void
169+
{
170+
$originalValue = new Number('13.37');
171+
$dbValue = $this->processValue(Types::NUMBER, $originalValue);
172+
173+
self::assertSame(0, $originalValue <=> $dbValue);
174+
}
175+
157176
private function processValue(string $type, mixed $originalValue): mixed
158177
{
159178
$columnName = 'test_' . $type;

‎tests/Functional/Types/NumberTest.php

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Tests\Functional\Types;
6+
7+
use BcMath\Number;
8+
use Doctrine\DBAL\Schema\Table;
9+
use Doctrine\DBAL\Tests\FunctionalTestCase;
10+
use Doctrine\DBAL\Types\Types;
11+
use PHPUnit\Framework\Attributes\RequiresPhp;
12+
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
13+
use PHPUnit\Framework\Attributes\TestWith;
14+
15+
#[RequiresPhp('8.4')]
16+
#[RequiresPhpExtension('bcmath')]
17+
final class NumberTest extends FunctionalTestCase
18+
{
19+
#[TestWith(['13.37'])]
20+
#[TestWith(['13.0'])]
21+
public function testInsertAndRetrieveNumber(string $numberAsString): void
22+
{
23+
$expected = new Number($numberAsString);
24+
25+
$table = new Table('number_table');
26+
$table->addColumn('val', Types::NUMBER, ['precision' => 4, 'scale' => 2]);
27+
28+
$this->dropAndCreateTable($table);
29+
30+
$this->connection->insert(
31+
'number_table',
32+
['val' => $expected],
33+
['val' => Types::NUMBER],
34+
);
35+
36+
$value = $this->connection->convertToPHPValue(
37+
$this->connection->fetchOne('SELECT val FROM number_table'),
38+
Types::NUMBER,
39+
);
40+
41+
self::assertInstanceOf(Number::class, $value);
42+
self::assertSame(0, $expected <=> $value);
43+
}
44+
45+
public function testCompareNumberTable(): void
46+
{
47+
$table = new Table('number_table');
48+
$table->addColumn('val', Types::NUMBER, ['precision' => 4, 'scale' => 2]);
49+
50+
$this->dropAndCreateTable($table);
51+
52+
$schemaManager = $this->connection->createSchemaManager();
53+
54+
self::assertTrue(
55+
$schemaManager->createComparator()
56+
->compareTables($schemaManager->introspectTable('number_table'), $table)
57+
->isEmpty(),
58+
);
59+
}
60+
}

‎tests/Platforms/PostgreSQLPlatformTest.php

+44
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
use Doctrine\DBAL\Platforms\AbstractPlatform;
88
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
99
use Doctrine\DBAL\Schema\Column;
10+
use Doctrine\DBAL\Schema\ColumnDiff;
1011
use Doctrine\DBAL\Schema\Sequence;
1112
use Doctrine\DBAL\Schema\Table;
13+
use Doctrine\DBAL\Schema\TableDiff;
1214
use Doctrine\DBAL\TransactionIsolationLevel;
1315
use Doctrine\DBAL\Types\Type;
1416
use Doctrine\DBAL\Types\Types;
@@ -676,4 +678,46 @@ public function testGetListSequencesSQL(): void
676678
$this->platform->getListSequencesSQL('test_db'),
677679
);
678680
}
681+
682+
public function testAlterTableChangeJsonToJsonb(): void
683+
{
684+
$table = new Table('mytable');
685+
$table->addColumn('payload', Types::JSON);
686+
687+
$tableDiff = new TableDiff($table, changedColumns: [
688+
'payload' => new ColumnDiff(
689+
$table->getColumn('payload'),
690+
(new Column(
691+
'payload',
692+
Type::getType(Types::JSON),
693+
))->setPlatformOption('jsonb', true),
694+
),
695+
]);
696+
697+
self::assertSame(
698+
['ALTER TABLE mytable ALTER payload TYPE JSONB'],
699+
$this->platform->getAlterTableSQL($tableDiff),
700+
);
701+
}
702+
703+
public function testAlterTableChangeJsonbToJson(): void
704+
{
705+
$table = new Table('mytable');
706+
$table->addColumn('payload', Types::JSON)->setPlatformOption('jsonb', true);
707+
708+
$tableDiff = new TableDiff($table, changedColumns: [
709+
'payload' => new ColumnDiff(
710+
$table->getColumn('payload'),
711+
(new Column(
712+
'payload',
713+
Type::getType(Types::JSON),
714+
)),
715+
),
716+
]);
717+
718+
self::assertSame(
719+
['ALTER TABLE mytable ALTER payload TYPE JSON'],
720+
$this->platform->getAlterTableSQL($tableDiff),
721+
);
722+
}
679723
}

‎tests/Schema/AbstractAssetTest.php

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ class AbstractAssetTest extends TestCase
1313
public function testInvalidName(): void
1414
{
1515
$this->expectException(InvalidName::class);
16+
17+
// @phpstan-ignore expr.resultUnused
1618
new Identifier(' ');
1719
}
1820
}

‎tests/Schema/AbstractNamedObjectTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public function testEmptyName(): void
1818
{
1919
$this->expectException(InvalidName::class);
2020

21+
// @phpstan-ignore expr.resultUnused
2122
new /** @extends AbstractNamedObject<Name> */
2223
class ('') extends AbstractNamedObject {
2324
};

‎tests/Types/NumberTest.php

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\DBAL\Tests\Types;
6+
7+
use BcMath\Number;
8+
use Doctrine\DBAL\Platforms\AbstractPlatform;
9+
use Doctrine\DBAL\Types\Exception\InvalidType;
10+
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
11+
use Doctrine\DBAL\Types\NumberType;
12+
use PHPUnit\Framework\Attributes\RequiresPhp;
13+
use PHPUnit\Framework\Attributes\RequiresPhpExtension;
14+
use PHPUnit\Framework\Attributes\TestWith;
15+
use PHPUnit\Framework\MockObject\MockObject;
16+
use PHPUnit\Framework\TestCase;
17+
use stdClass;
18+
19+
#[RequiresPhp('8.4')]
20+
#[RequiresPhpExtension('bcmath')]
21+
final class NumberTest extends TestCase
22+
{
23+
private AbstractPlatform&MockObject $platform;
24+
private NumberType $type;
25+
26+
protected function setUp(): void
27+
{
28+
$this->platform = $this->createMock(AbstractPlatform::class);
29+
$this->type = new NumberType();
30+
}
31+
32+
#[TestWith(['5.5'])]
33+
#[TestWith(['5.5000'])]
34+
#[TestWith([5.5])]
35+
public function testDecimalConvertsToPHPValue(mixed $dbValue): void
36+
{
37+
$phpValue = $this->type->convertToPHPValue($dbValue, $this->platform);
38+
39+
self::assertInstanceOf(Number::class, $phpValue);
40+
self::assertSame(0, $phpValue <=> new Number('5.5'));
41+
}
42+
43+
public function testDecimalNullConvertsToPHPValue(): void
44+
{
45+
self::assertNull($this->type->convertToPHPValue(null, $this->platform));
46+
}
47+
48+
public function testNumberConvertsToDecimalString(): void
49+
{
50+
self::assertSame('5.5', $this->type->convertToDatabaseValue(new Number('5.5'), $this->platform));
51+
}
52+
53+
public function testNumberNullConvertsToNull(): void
54+
{
55+
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform));
56+
}
57+
58+
#[TestWith(['5.5'])]
59+
#[TestWith([new stdClass()])]
60+
public function testInvalidPhpValuesTriggerException(mixed $value): void
61+
{
62+
self::expectException(InvalidType::class);
63+
64+
$this->type->convertToDatabaseValue($value, $this->platform);
65+
}
66+
67+
#[TestWith(['foo'])]
68+
#[TestWith([true])]
69+
public function testUnexpectedValuesReturnedByTheDatabaseTriggerException(mixed $value): void
70+
{
71+
self::expectException(ValueNotConvertible::class);
72+
73+
$this->type->convertToPHPValue($value, $this->platform);
74+
}
75+
}

0 commit comments

Comments
 (0)
Please sign in to comment.