Skip to content

Commit

Permalink
Initial testing
Browse files Browse the repository at this point in the history
  • Loading branch information
bigfoot90 committed Mar 24, 2024
1 parent ccdb800 commit 08bca04
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 44 deletions.
3 changes: 3 additions & 0 deletions src/Schema/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Column extends AbstractAsset

protected ?int $_length = null;

/** @var string[] */
protected array $_members = [];

protected ?int $_precision = null;
Expand Down Expand Up @@ -221,11 +222,13 @@ public function setAutoincrement(bool $flag): self
return $this;
}

/** @return string[] */
public function getMembers(): array
{
return $this->_members;
}

/** @param string[] $members */
public function setMembers(array $members): void
{
$this->_members = $members;
Expand Down
1 change: 1 addition & 0 deletions src/Schema/MySQLSchemaManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use function implode;
use function is_string;
use function preg_match;
use function preg_match_all;
use function str_contains;
use function strtok;
use function strtolower;
Expand Down
100 changes: 56 additions & 44 deletions src/Types/EnumType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,106 +4,118 @@

namespace Doctrine\DBAL\Types;

use BackedEnum;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\PostgreSqlPlatform;

Check failure on line 9 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Class Doctrine\DBAL\Platforms\PostgreSQLPlatform referenced with incorrect case: Doctrine\DBAL\Platforms\PostgreSqlPlatform.
use Doctrine\DBAL\Platforms\SqlitePlatform;

Check failure on line 10 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Class Doctrine\DBAL\Platforms\SQLitePlatform referenced with incorrect case: Doctrine\DBAL\Platforms\SqlitePlatform.
use Doctrine\DBAL\Platforms\SQLServerPlatform;
use Doctrine\DBAL\Types\Exception\InvalidType;
use Doctrine\DBAL\Types\Exception\ValueNotConvertible;
use ReflectionClass;
use Throwable;
use UnitEnum;
use ValueError;

use function array_map;
use function class_exists;
use function enum_exists;
use function implode;
use function is_string;
use function sprintf;

final class EnumType extends Type
{
public string $name = 'enum';

public ?string $enumClassname = null;

/** @var array<int, string> */
public array $members = [];

/**
* Gets an array of database types that map to this Doctrine type.
*
* @return string[]
* {@inheritDoc}
*/
public function getMappedDatabaseTypes(AbstractPlatform $platform): array
{
return [$this->name];
}

/**
* Gets the SQL declaration snippet for a field of this type.
*
* @param mixed[] $column The field declaration
* @param AbstractPlatform $platform The currently used database platform
* {@inheritDoc}
*/
public function getSqlDeclaration(array $column, AbstractPlatform $platform): string

Check failure on line 46 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Method Doctrine\DBAL\Types\EnumType::getSqlDeclaration() does not match parent method name: Doctrine\DBAL\Types\Type::getSQLDeclaration().
{
assert($column['type'] instanceof self::class);

$values = implode(
', ',
array_map(
static fn (string $value) => "'{$value}'",
$column['members'] ?: $column['type']->members
)
static fn (string $value) => sprintf('\'%s\'', $value),
$column['members'] ?: $column['type']->members,

Check failure on line 52 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Short ternary operator is not allowed. Use null coalesce operator if applicable or consider using long ternary.
),
);

$sqlDeclaration = match (true) {
return match (true) {
$platform instanceof SqlitePlatform => sprintf('TEXT CHECK(%s IN (%s))', $column['name'], $values),

Check failure on line 57 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Class Doctrine\DBAL\Platforms\SQLitePlatform referenced with incorrect case: Doctrine\DBAL\Platforms\SqlitePlatform.

Check failure on line 57 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm (8.3)

InvalidClass

src/Types/EnumType.php:57:34: InvalidClass: Class, interface or enum Doctrine\DBAL\Platforms\SqlitePlatform has wrong casing (see https://psalm.dev/007)
$platform instanceof PostgreSqlPlatform, $platform instanceof SQLServerPlatform => sprintf('VARCHAR(255) CHECK(%s IN (%s))', $column['name'], $values),

Check warning on line 58 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Line exceeds 120 characters; contains 163 characters

Check failure on line 58 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Class Doctrine\DBAL\Platforms\PostgreSQLPlatform referenced with incorrect case: Doctrine\DBAL\Platforms\PostgreSqlPlatform.

Check failure on line 58 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm (8.3)

InvalidClass

src/Types/EnumType.php:58:34: InvalidClass: Class, interface or enum Doctrine\DBAL\Platforms\PostgreSqlPlatform has wrong casing (see https://psalm.dev/007)
default => sprintf('ENUM(%s)', $values),
};

return $sqlDeclaration;
}

/**
* {@inheritdoc}
*
* @return mixed the database representation of the value
*
* @throws \InvalidArgumentException
* {@inheritDoc}
*/
public function convertToDatabaseValue($value, AbstractPlatform $platform): ?string
{
if (null === $value) {
if ($value === null) {
return null;
}

if ($value instanceof UnitEnum) {
if ($value instanceof BackedEnum) {
return $value->value;

Check failure on line 74 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Method Doctrine\DBAL\Types\EnumType::convertToDatabaseValue() should return string|null but returns int|string.

Check failure on line 74 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm (8.3)

InvalidReturnStatement

src/Types/EnumType.php:74:24: InvalidReturnStatement: The inferred type 'int|string' does not match the declared return type 'null|string' for Doctrine\DBAL\Types\EnumType::convertToDatabaseValue (see https://psalm.dev/128)
}

return $value->name;
}

return (string) $value;
}

/**
* {@inheritdoc}
*
* @return mixed the PHP representation of the value
*/
public function convertToPHPValue(mixed $value, AbstractPlatform $platform): mixed
{
if (null === $value) {
if ($value === null) {
return null;
}

if (!\is_string($value)) {
if (! is_string($value)) {
throw InvalidType::new($value, $this->name, ['null', 'string']);

Check warning on line 90 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L90

Added line #L90 was not covered by tests
}

$refl = new \ReflectionClass($this->enumClassname);

try {
return $refl->newInstance($value);
} catch (\Throwable $e) {
throw ValueNotConvertible::new($value, $this->name, $e->getMessage(), $e);
if ($this->enumClassname) {

Check failure on line 93 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Only booleans are allowed in an if condition, string|null given.

Check failure on line 93 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm (8.3)

RiskyTruthyFalsyComparison

src/Types/EnumType.php:93:13: RiskyTruthyFalsyComparison: Operand of type null|string contains type string, which can be falsy and truthy. This can cause possibly unexpected behavior. Use strict comparison instead. (see https://psalm.dev/356)
if (enum_exists($this->enumClassname)) {
try {
foreach ($this->enumClassname::cases() as $case) {
if (($case instanceof BackedEnum && $value === $case->value) or $value === $case->name) {

Check failure on line 97 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Logical operator "or" is prohibited; use "||" instead
return $case;
}
}

throw new ValueError(sprintf("'%s' is not a valid backing value for enum %s", $value, $this->enumClassname));

Check warning on line 102 in src/Types/EnumType.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Line exceeds 120 characters; contains 129 characters
} catch (Throwable $e) {
throw ValueNotConvertible::new($value, $this->name, $e->getMessage(), $e);

Check warning on line 104 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L102-L104

Added lines #L102 - L104 were not covered by tests
}
}

if (class_exists($this->enumClassname)) {
$refl = new ReflectionClass($this->enumClassname);

try {
return $refl->newInstance($value);
} catch (Throwable $e) {
throw ValueNotConvertible::new($value, $this->name, $e->getMessage(), $e);

Check warning on line 114 in src/Types/EnumType.php

View check run for this annotation

Codecov / codecov/patch

src/Types/EnumType.php#L113-L114

Added lines #L113 - L114 were not covered by tests
}
}
}
}

/**
* {@inheritdoc}
*/
public static function addType(string $name, string $enumClassname): void
{
self::getTypeRegistry()->register($name, $me = new self());
$me->name = $name;
$me->enumClassname = $enumClassname;
$me->members = $enumClassname::getAllowedValues();
return $value;
}
}
32 changes: 32 additions & 0 deletions tests/Functional/Types/EnumTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Functional\Types;

use Doctrine\DBAL\Schema\Table;
use Doctrine\DBAL\Tests\FunctionalTestCase;
use Doctrine\DBAL\Types\Types;

class EnumTest extends FunctionalTestCase
{
protected function setUp(): void
{
$table = new Table('enum_table');
$table->addColumn('val', Types::ENUM, ['members' => ['a', 'b']]);

$this->dropAndCreateTable($table);
}

public function testInsertAndSelect(): void
{
$val = 'b';

$result = $this->connection->insert('enum_table', ['val' => $val]);
self::assertSame(1, $result);

$value = $this->connection->fetchOne('SELECT val FROM enum_table');

self::assertEquals($val, $value);
}
}
14 changes: 14 additions & 0 deletions tests/Schema/ColumnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public function testToArray(): void
'default' => 'baz',
'notnull' => false,
'length' => 200,
'members' => [],
'precision' => 5,
'scale' => 2,
'fixed' => true,
Expand Down Expand Up @@ -148,4 +149,17 @@ public function testColumnComment(): void
self::assertArrayHasKey('comment', $columnArray);
self::assertEquals('foo', $columnArray['comment']);
}

public function testEnumMembers(): void
{
$column = new Column('bar', Type::getType(Types::ENUM));
self::assertSame([], $column->getMembers());

$column->setMembers(['a', 'b']);
self::assertEquals(['a', 'b'], $column->getMembers());

$columnArray = $column->toArray();
self::assertArrayHasKey('members', $columnArray);
self::assertEquals(['a', 'b'], $columnArray['members']);
}
}
118 changes: 118 additions & 0 deletions tests/Types/EnumTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

Check warning on line 1 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

A file should declare new symbols (classes, functions, constants, etc.) and cause no other side effects, or it should execute logic with side effects, but should not do both. The first symbol is defined on line 14 and the first side effect is on line 118.

declare(strict_types=1);

namespace Doctrine\DBAL\Tests\Types;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\EnumType;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;

use function anonymous_class;

Check failure on line 12 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Type anonymous_class is not used in this file.

Check failure on line 12 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Used function anonymous_class not found.

class EnumTest extends TestCase
{
private AbstractPlatform&MockObject $platform;
private EnumType $type;

protected function setUp(): void
{
$this->platform = $this->createMock(AbstractPlatform::class);
$this->type = new EnumType();
}

public function testReturnsSQLDeclaration(): void
{
self::assertSame('ENUM(\'a\', \'b\')', $this->type->getSQLDeclaration(['members' => ['a', 'b']], $this->platform));

Check warning on line 27 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Line exceeds 120 characters; contains 123 characters

Check failure on line 27 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with PHPStan (8.3)

Call to method Doctrine\DBAL\Types\EnumType::getSqlDeclaration() with incorrect case: getSQLDeclaration
}

public function testConvertToPHPValue(): void
{
self::assertIsString($this->type->convertToPHPValue('foo', $this->platform));
self::assertIsString($this->type->convertToPHPValue('', $this->platform));
self::assertNull($this->type->convertToPHPValue(null, $this->platform));
}

public function testConvertToPHPEnum(): void
{
$this->type->enumClassname = EnumPhp::class;

self::assertInstanceOf($this->type->enumClassname, $this->type->convertToPHPValue('A', $this->platform));
self::assertSame(EnumPhp::A, $this->type->convertToPHPValue('A', $this->platform));
self::assertNull($this->type->convertToPHPValue(null, $this->platform));
}

public function testConvertToPHPEnumBacked(): void
{
$this->type->enumClassname = EnumPhpBacked::class;

self::assertInstanceOf($this->type->enumClassname, $this->type->convertToPHPValue('a', $this->platform));
self::assertSame(EnumPhpBacked::A, $this->type->convertToPHPValue('a', $this->platform));
self::assertNull($this->type->convertToPHPValue(null, $this->platform));
}

public function testConvertToPHPObject(): void
{
$this->type->enumClassname = EnumClass::class;

self::assertInstanceOf($this->type->enumClassname, $this->type->convertToPHPValue('a', $this->platform));
self::assertEquals(new EnumClass('a'), $this->type->convertToPHPValue('a', $this->platform));
self::assertNull($this->type->convertToPHPValue(null, $this->platform));
}

public function testConvertStringToDatabaseValue(): void
{
self::assertSame('a', $this->type->convertToDatabaseValue('a', $this->platform));
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform));
}

public function testConvertEnumToDatabaseValue(): void
{
$this->type->enumClassname = EnumPhp::class;

self::assertSame('A', $this->type->convertToDatabaseValue(EnumPhp::A, $this->platform));
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform));
}

public function testConvertEnumBackedToDatabaseValue(): void
{
$this->type->enumClassname = EnumPhpBacked::class;

self::assertSame('a', $this->type->convertToDatabaseValue(EnumPhpBacked::A, $this->platform));
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform));
}

public function testConvertObjectToDatabaseValue(): void
{
$this->type->enumClassname = EnumClass::class;

self::assertSame('a', $this->type->convertToDatabaseValue(new EnumClass('a'), $this->platform));
self::assertNull($this->type->convertToDatabaseValue(null, $this->platform));
}
}

enum EnumPhp
{
case A;
case B;
}


enum EnumPhpBacked: string
{
case A = 'a';
case B = 'b';
}

final class EnumClass implements \Stringable {

Check failure on line 108 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Class \Stringable should not be referenced via a fully qualified name, but via a use statement.

Check failure on line 108 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Opening brace of a class must be on the line after the definition
function __construct(

Check failure on line 109 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Visibility must be declared on method "__construct"
private ?string $value

Check failure on line 110 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Multi-line function declaration must have a trailing comma after the last parameter.
) {
}

function __tostring(): string

Check failure on line 114 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Visibility must be declared on method "__tostring"

Check failure on line 114 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm (8.3)

InvalidToString

tests/Types/EnumTest.php:114:28: InvalidToString: __toString methods must return a string, null|string returned (see https://psalm.dev/055)
{
return $this->value;

Check failure on line 116 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Static Analysis with Psalm (8.3)

NullableReturnStatement

tests/Types/EnumTest.php:116:16: NullableReturnStatement: The declared return type 'string' for Doctrine\DBAL\Tests\Types\EnumClass::__tostring is not nullable, but the function returns 'null|string' (see https://psalm.dev/139)
}
};

Check failure on line 118 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Closing brace must not be followed by any comment or statement on the same line

Check failure on line 118 in tests/Types/EnumTest.php

View workflow job for this annotation

GitHub Actions / Coding Standards / Coding Standards (8.3)

Useless semicolon.

0 comments on commit 08bca04

Please sign in to comment.