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
4 changes: 4 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ jobs:
- lowest
- highest
php-version:
- '7.2'
- '7.3'
- '7.4'
- '8.0'
- '8.1'
- '8.2'
- '8.3'
Expand Down
10 changes: 6 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
}
],
"require": {
"php": ">=8.1",
"doctrine/orm": "^3.0",
"symfony/property-access": "^5.2 || ^6.0 || ^7.0"
"php": ">=7.2",
"doctrine/orm": "^2.17 || ^3.0",
"symfony/property-access": "^4.0 || ^5.2 || ^6.0 || ^7.0",
"symfony/polyfill-php80": "^1.20"
},
"require-dev": {
"phpspec/phpspec": "~6.3 || ^7.0"
"phpspec/phpspec": "~6.3 || ^7.0 || ^8.0",
"phpspec/prophecy": "^1.15"
},
"autoload": {
"psr-4": {
Expand Down
20 changes: 20 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -1,4 +1,24 @@
parameters:
level: 8
# Silences two instanceof.alwaysTrue reports:
#
# src/Query/QueryModifierCollection.php:44
# src/Result/ResultModifierCollection.php:43
#
# Both collections declare their variadic as "@param QueryModifier ...$children"
# without a native type, so PHPStan trusts the PHPDoc and considers the
# instanceof guard dead code. It is not: PHPDoc is not enforced at runtime,
# these are public API classes constructed only by consumers, and the guard is
# the only thing that turns a bad argument into an InvalidArgumentException
# with a useful message.
#
# The proper fix is a native type - __construct(QueryModifier ...$children) -
# which would let PHP enforce it and make the guard genuinely removable. That
# changes the error contract from InvalidArgumentException to TypeError, so it
# has to wait for a major release.
#
# Scope: as of this commit the setting changes exactly those two reports and
# nothing else in src/.
treatPhpDocTypesAsCertain: false
paths:
- src
2 changes: 1 addition & 1 deletion src/DBALTypesResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static function tryGetTypeForValue($value): ?Type
$classNameParts = explode('\\', str_replace('_', '\\', $className));
$typeName = array_pop($classNameParts);

if (null !== $typeName && Type::hasType($typeName)) {
if (Type::hasType($typeName)) {
return Type::getType($typeName);
}

Expand Down
4 changes: 1 addition & 3 deletions src/Exception/InvalidArgumentException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@

namespace Happyr\DoctrineSpecification\Exception;

final class InvalidArgumentException extends \InvalidArgumentException
{
}
final class InvalidArgumentException extends \InvalidArgumentException {}
4 changes: 1 addition & 3 deletions src/Exception/LogicException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@

namespace Happyr\DoctrineSpecification\Exception;

final class LogicException extends \LogicException
{
}
final class LogicException extends \LogicException {}
4 changes: 1 addition & 3 deletions src/Exception/NoResultException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@

namespace Happyr\DoctrineSpecification\Exception;

final class NoResultException extends UnexpectedResultException
{
}
final class NoResultException extends UnexpectedResultException {}
4 changes: 1 addition & 3 deletions src/Exception/NonUniqueResultException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@

namespace Happyr\DoctrineSpecification\Exception;

final class NonUniqueResultException extends UnexpectedResultException
{
}
final class NonUniqueResultException extends UnexpectedResultException {}
4 changes: 1 addition & 3 deletions src/Exception/OperandNotExecuteException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@

namespace Happyr\DoctrineSpecification\Exception;

final class OperandNotExecuteException extends \RuntimeException
{
}
final class OperandNotExecuteException extends \RuntimeException {}
4 changes: 1 addition & 3 deletions src/Exception/PlatformFunctionExecutorException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@

namespace Happyr\DoctrineSpecification\Exception;

final class PlatformFunctionExecutorException extends \InvalidArgumentException
{
}
final class PlatformFunctionExecutorException extends \InvalidArgumentException {}
4 changes: 1 addition & 3 deletions src/Exception/UnexpectedResultException.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,4 @@

namespace Happyr\DoctrineSpecification\Exception;

abstract class UnexpectedResultException extends \RuntimeException
{
}
abstract class UnexpectedResultException extends \RuntimeException {}
2 changes: 1 addition & 1 deletion src/Filter/Like.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ final class Like implements Filter, Satisfiable
*/
public function __construct($field, $value, string $format = LikePattern::CONTAINS, ?string $context = null)
{
if (!($value instanceof LikePattern)) {
if (!$value instanceof LikePattern) {
$value = new LikePattern($value, $format);
}

Expand Down
6 changes: 4 additions & 2 deletions src/Filter/Satisfiable.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ interface Satisfiable
* @param iterable $collection
* @param string|null $context
*
* @return iterable
*
* @phpstan-template T
*
* @phpstan-param iterable<T> $collection
*
* @return iterable
*
* @phpstan-return iterable<T>
*/
public function filterCollection(iterable $collection, ?string $context = null): iterable;
Expand Down
2 changes: 1 addition & 1 deletion src/Operand/ArgumentToOperandConverter.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static function toValue($argument): Operand
public static function isAllOperands(array $arguments): bool
{
foreach ($arguments as $argument) {
if (!($argument instanceof Operand)) {
if (!$argument instanceof Operand) {
return false;
}
}
Expand Down
1 change: 1 addition & 0 deletions src/Operand/PlatformFunction/DateSub.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ public function execute($candidate, ?string $context = null): \DateTimeImmutable
if (0 > (float) $value) {
return $new_date->modify(sprintf('+%d %s', abs((float) $value), $this->unit));
}

return $new_date->modify(sprintf('-%d %s', $value, $this->unit));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ final class PlatformFunctionExecutorRegistry
{
/**
* @var callable[]
*
* @phpstan-var array<string, callable>
*/
private $executors;

/**
* @param callable[] $executors
*
* @phpstan-param array<string, callable> $executors
*/
public function __construct(array $executors)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ final class SubstringExecutor
* @param int $offset
* @param int|null $length
*
* @return false|string
* @return string
*/
public function __invoke(string $string, int $offset, ?int $length = null)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Query/IndexBy.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ final class IndexBy implements QueryModifier
*/
public function __construct($field, ?string $context = null)
{
if (!($field instanceof Field)) {
if (!$field instanceof Field) {
$field = new Field($field);
}

Expand Down
3 changes: 2 additions & 1 deletion src/Repository/EntitySpecificationRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
/**
* This class allows you to use a Specification to query entities.
*
* @template T
* @template T of object
*
* @phpstan-extends EntityRepository<T>
*/
class EntitySpecificationRepository extends EntityRepository implements EntitySpecificationRepositoryInterface
Expand Down
6 changes: 4 additions & 2 deletions src/Repository/RepositoryFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ class RepositoryFactory implements RepositoryFactoryInterface
* @param EntityManagerInterface $entityManager the EntityManager instance
* @param string $entityName the name of the entity
*
* @return EntityRepository
* @phpstan-template T of object
*
* @phpstan-template T
* @phpstan-param class-string<T> $entityName
*
* @return EntityRepository
*
* @phpstan-return EntityRepository<T>
*/
public function getRepository(EntityManagerInterface $entityManager, $entityName): EntityRepository
Expand Down
4 changes: 1 addition & 3 deletions src/Specification/Specification.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,4 @@
use Happyr\DoctrineSpecification\Filter\Satisfiable;
use Happyr\DoctrineSpecification\Query\QueryModifier;

interface Specification extends Filter, QueryModifier, Satisfiable
{
}
interface Specification extends Filter, QueryModifier, Satisfiable {}
10 changes: 5 additions & 5 deletions tests/DQLContextResolverSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,10 @@ public function it_resolve_conflict(QueryBuilder $qb): void
new Join(Join::INNER_JOIN, 'foo.bar', 'contestant'),
],
]);
$qb->join('root.contestant', Argument::that(function ($argument) {
$qb->join('root.contestant', Argument::that(static function ($argument) {
return preg_match('/^contestant[a-f0-9]+/', $argument);
}))->willReturn($qb);
$qb->join(Argument::that(function ($argument) {
$qb->join(Argument::that(static function ($argument) {
return preg_match('/^contestant[a-f0-9]+\.contest$/', $argument);
}), 'contest')->willReturn($qb);

Expand All @@ -132,12 +132,12 @@ public function it_join_always_unique_alias(QueryBuilder $qb): void

$qb->getAllAliases()->willReturn([]);
$qb->getDQLPart('join')->willReturn([]);
$qb->join('root.contestant', Argument::that(function ($argument) {
$qb->join('root.contestant', Argument::that(static function ($argument) {
return preg_match('/^contestant[a-f0-9]+/', $argument);
}))->willReturn($qb);
$qb->join(Argument::that(function ($argument) {
$qb->join(Argument::that(static function ($argument) {
return preg_match('/^contestant[a-f0-9]+\.contest$/', $argument);
}), Argument::that(function ($argument) {
}), Argument::that(static function ($argument) {
return preg_match('/^contest[a-f0-9]+$/', $argument);
}))->willReturn($qb);

Expand Down
4 changes: 2 additions & 2 deletions tests/Filter/InstanceOfXSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ public function it_filter_array_collection(): void
$this->beConstructedWith(Player::class, null);

$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001],
];

Expand Down
20 changes: 10 additions & 10 deletions tests/Filter/IsEmptySpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ public function it_filter_array_collection(): void
$this->beConstructedWith('points', null);

$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => null],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => null],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001],
];

Expand All @@ -90,8 +90,8 @@ public function it_is_satisfied_with_array(): void
{
$this->beConstructedWith('points', null);

$playerA = ['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500];
$playerB = ['pseudo' => 'Moe', 'gender' => 'M', 'points' => null];
$playerA = ['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500];
$playerB = ['pseudo' => 'Moe', 'gender' => 'M', 'points' => null];
$playerC = ['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001];

$this->isSatisfiedBy($playerA)->shouldBe(false);
Expand Down Expand Up @@ -138,8 +138,8 @@ public function it_filter_array_collection_in_context(): void
$tetris = ['name' => 'Tetris', 'releaseAt' => null];
$mahjong = ['name' => 'Mahjong', 'releaseAt' => $releaseAt];
$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001, 'inGame' => $tetris],
];

Expand All @@ -154,8 +154,8 @@ public function it_filter_array_collection_in_global_context(): void
$tetris = ['name' => 'Tetris', 'releaseAt' => null];
$mahjong = ['name' => 'Mahjong', 'releaseAt' => $releaseAt];
$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001, 'inGame' => $tetris],
];

Expand All @@ -181,8 +181,8 @@ public function it_filter_array_collection_in_combo_context(): void
$tetris = ['name' => 'Tetris', 'owner' => $tetrisOwner];
$mahjong = ['name' => 'Mahjong', 'owner' => $mahjongOwner];
$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001, 'inGame' => $tetris],
];

Expand Down
20 changes: 10 additions & 10 deletions tests/Filter/IsNotNullSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ public function it_filter_array_collection(): void
$this->beConstructedWith('points', null);

$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => null],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => null],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001],
];

Expand All @@ -96,8 +96,8 @@ public function it_is_satisfied_with_array(): void
{
$this->beConstructedWith('points', null);

$playerA = ['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500];
$playerB = ['pseudo' => 'Moe', 'gender' => 'M', 'points' => null];
$playerA = ['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500];
$playerB = ['pseudo' => 'Moe', 'gender' => 'M', 'points' => null];
$playerC = ['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001];

$this->isSatisfiedBy($playerA)->shouldBe(true);
Expand Down Expand Up @@ -146,8 +146,8 @@ public function it_filter_array_collection_in_context(): void
$tetris = ['name' => 'Tetris', 'releaseAt' => $releaseAt];
$mahjong = ['name' => 'Mahjong', 'releaseAt' => null];
$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001, 'inGame' => $tetris],
];

Expand All @@ -162,8 +162,8 @@ public function it_filter_array_collection_in_global_context(): void
$tetris = ['name' => 'Tetris', 'releaseAt' => $releaseAt];
$mahjong = ['name' => 'Mahjong', 'releaseAt' => null];
$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001, 'inGame' => $tetris],
];

Expand All @@ -190,8 +190,8 @@ public function it_filter_array_collection_in_combo_context(): void
$tetris = ['name' => 'Tetris', 'owner' => $tetrisOwner];
$mahjong = ['name' => 'Mahjong', 'owner' => $mahjongOwner];
$players = [
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Joe', 'gender' => 'M', 'points' => 2500, 'inGame' => $mahjong],
['pseudo' => 'Moe', 'gender' => 'M', 'points' => 1230, 'inGame' => $mahjong],
['pseudo' => 'Alice', 'gender' => 'F', 'points' => 9001, 'inGame' => $tetris],
];

Expand Down
Loading
Loading