Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions analyzer-baseline.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3108,12 +3108,6 @@ code = "overly-wide-return-type"
message = 'Declared return type `PhpDb\Sql\Platform\PlatformDecoratorInterface|null` for `getsqlplatform` has unused branches: `null`.'
count = 1

[[issues]]
file = "src/Sql/Sql.php"
code = "possibly-invalid-argument"
message = 'Possible argument type mismatch for argument #1 of `PhpDb\Sql\Exception\InvalidArgumentException::forForeignTable`: expected `string`, but possibly received `PhpDb\Sql\TableIdentifier|array<string, PhpDb\Sql\TableIdentifier|string>|string`.'
count = 4

[[issues]]
file = "src/Sql/Sql.php"
code = "unhandled-thrown-type"
Expand Down
34 changes: 32 additions & 2 deletions src/Sql/Exception/InvalidArgumentException.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
namespace PhpDb\Sql\Exception;

use PhpDb\Exception;
use PhpDb\Sql\TableIdentifier;

use function array_key_first;
use function get_debug_type;
use function is_string;
use function sprintf;

class InvalidArgumentException extends Exception\InvalidArgumentException
Expand Down Expand Up @@ -111,9 +115,12 @@
return new self(self::EMPTY_TABLE);
}

public static function forForeignTable(string $table): self
/**
* @param string|TableIdentifier|array<string, string|TableIdentifier> $table
*/
public static function forForeignTable(string|TableIdentifier|array $table): self
{
return new self(sprintf(self::FOREIGN_TABLE, $table));
return new self(sprintf(self::FOREIGN_TABLE, self::describeTable($table)));
}

public static function forInvalidArgumentType(): self
Expand Down Expand Up @@ -195,4 +202,27 @@
{
return new self(self::VALUES_WITH_MERGE_FLAG);
}

/**
* @param string|TableIdentifier|array<string, string|TableIdentifier> $table
*/
private static function describeTable(string|TableIdentifier|array $table): string
{
if (is_string($table)) {
return $table;
}

if ($table instanceof TableIdentifier) {
[$name, $schema] = $table->getTableAndSchema();

return null === $schema ? $name : "{$schema}.{$name}";
}

$alias = (string) array_key_first($table);

Check warning on line 221 in src/Sql/Exception/InvalidArgumentException.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "CastString": @@ @@ return null === $schema ? $name : "{$schema}.{$name}"; } - $alias = (string) array_key_first($table); + $alias = array_key_first($table); $target = $table[$alias] ?? null; return null === $target
$target = $table[$alias] ?? null;

return null === $target
? get_debug_type($target)
: self::describeTable($target) . " AS {$alias}";
}
}
30 changes: 30 additions & 0 deletions test/unit/Sql/Exception/InvalidArgumentExceptionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use PhpDb\Exception\ExceptionInterface;
use PhpDb\Sql\Exception\InvalidArgumentException;
use PhpDb\Sql\TableIdentifier;
use PHPUnit\Framework\Attributes\CoversMethod;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
Expand All @@ -26,6 +27,7 @@
#[CoversMethod(InvalidArgumentException::class, 'forEmptySeparator')]
#[CoversMethod(InvalidArgumentException::class, 'forEmptyTable')]
#[CoversMethod(InvalidArgumentException::class, 'forForeignTable')]
#[CoversMethod(InvalidArgumentException::class, 'describeTable')]
#[CoversMethod(InvalidArgumentException::class, 'forInvalidArgumentType')]
#[CoversMethod(InvalidArgumentException::class, 'forInvalidFromArray')]
#[CoversMethod(InvalidArgumentException::class, 'forInvalidJoinName')]
Expand All @@ -41,6 +43,19 @@
#[CoversMethod(InvalidArgumentException::class, 'forValuesWithMergeFlag')]
final class InvalidArgumentExceptionTest extends TestCase
{
/** @return array<string, array{string|TableIdentifier|array<string, string|TableIdentifier>, string}> */
public static function foreignTableProvider(): array
{
return [
'table name' => ['users', 'users'],
'table identifier' => [new TableIdentifier('users'), 'users'],
'table identifier with schema' => [new TableIdentifier('users', 'app'), 'app.users'],
'aliased table name' => [['u' => 'users'], 'users AS u'],
'aliased table identifier' => [['u' => new TableIdentifier('users', 'app')], 'app.users AS u'],
'empty array' => [[], 'null'],
];
}

/** @return array<string, array{string, list<string|int>, string}> */
public static function namedConstructorProvider(): array
{
Expand Down Expand Up @@ -168,6 +183,21 @@ public static function namedConstructorProvider(): array
];
}

/**
* @param string|TableIdentifier|array<string, string|TableIdentifier> $table
*/
#[Test]
#[DataProvider('foreignTableProvider')]
public function forForeignTableDescribesEachKindOfTable(
string|TableIdentifier|array $table,
string $described,
): void {
self::assertSame(
sprintf(InvalidArgumentException::FOREIGN_TABLE, $described),
InvalidArgumentException::forForeignTable($table)->getMessage(),
);
}

/** @param list<string|int> $arguments */
#[Test]
#[DataProvider('namedConstructorProvider')]
Expand Down
28 changes: 28 additions & 0 deletions test/unit/Sql/SqlTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ public static function dataChangingStatementProvider(): array
];
}

/** @return array<string, array{array<string, string|TableIdentifier>|TableIdentifier, string}> */
public static function foreignTableProvider(): array
{
return [
'table identifier with schema' => [new TableIdentifier('foo', 'sch'), 'sch.foo'],
'aliased table name' => [['f' => 'foo'], 'foo AS f'],
'aliased table identifier' => [['f' => new TableIdentifier('foo')], 'foo AS f'],
];
}

// @codingStandardsIgnoreStart
#[Test]
public function _construct(): void
Expand Down Expand Up @@ -144,6 +154,24 @@ public function deleteThrowsWhenTableConflicts(): void
$this->sql->delete(new TableIdentifier('bar'));
}

/**
* @param array<string, string|TableIdentifier>|TableIdentifier $table
*/
#[Test]
#[DataProvider('foreignTableProvider')]
public function foreignTableMessageDescribesTheConstructedTable(
array|TableIdentifier $table,
string $described,
): void {
$sql = new Sql($this->mockAdapter, $table);

self::expectException(InvalidArgumentException::class);
self::expectExceptionMessage(
"This Sql object is intended to work with only the table \"{$described}\" provided at construction time.",
);
$sql->select('bar');
}

#[Test]
public function getSqlPlatformReturnsPlatformDecorator(): void
{
Expand Down
Loading