Skip to content

Commit 82fe912

Browse files
xmarchegaynjoubert-cleverage
authored andcommitted
#11 Push to phpstan level 8
1 parent 7fb7c40 commit 82fe912

File tree

7 files changed

+78
-33
lines changed

7 files changed

+78
-33
lines changed

phpstan.neon

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,5 @@
11
parameters:
2-
level: 6
2+
level: 8
33
paths:
44
- src
55
- tests
6-
ignoreErrors:
7-
- '#type has no value type specified in iterable type#'
8-
- '#has parameter .* with no value type specified in iterable type#'
9-
- '#has no value type specified in iterable type array#'
10-
- '#configureOptions\(\) has no return type specified.#'
11-
- '#configure\(\) has no return type specified#'
12-
- '#process\(\) has no return type specified#'
13-
- '#should return Iterator but returns Traversable#'
14-
- '#Negated boolean expression is always false#'
15-
checkGenericClassInNonGenericObjectType: false
16-
reportUnmatchedIgnoredErrors: false
17-
inferPrivatePropertyTypeFromConstructor: true
18-
treatPhpDocTypesAsCertain: false

src/Task/Database/DatabaseReaderTask.php

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,26 @@
1919
use CleverAge\ProcessBundle\Model\ProcessState;
2020
use Doctrine\DBAL\Connection;
2121
use Doctrine\DBAL\Result;
22+
use Doctrine\DBAL\Types\Type;
2223
use Doctrine\Persistence\ManagerRegistry;
2324
use Psr\Log\LoggerInterface;
2425
use Psr\Log\LogLevel;
2526
use Symfony\Component\OptionsResolver\OptionsResolver;
2627

2728
/**
2829
* Fetch entities from doctrine.
30+
*
31+
* @phpstan-type Options array{
32+
* 'sql': ?string,
33+
* 'table': string,
34+
* 'limit': ?int,
35+
* 'empty_log_level': string,
36+
* 'paginate': ?int,
37+
* 'offset': ?int,
38+
* 'input_as_params': bool,
39+
* 'params': array<int<0, max>|string, mixed>,
40+
* 'types': array<int, int|string|Type|null>|array<string, int|string|Type|null>
41+
* }
2942
*/
3043
class DatabaseReaderTask extends AbstractConfigurableTask implements IterableTaskInterface, FinalizableTaskInterface
3144
{
@@ -42,7 +55,7 @@ public function __construct(
4255
/**
4356
* Moves the internal pointer to the next element,
4457
* return true if the task has a next element
45-
* return false if the task has terminated it's iteration.
58+
* return false if the task has terminated its iteration.
4659
*/
4760
public function next(ProcessState $state): bool
4861
{
@@ -57,6 +70,7 @@ public function next(ProcessState $state): bool
5770

5871
public function execute(ProcessState $state): void
5972
{
73+
/** @var Options $options */
6074
$options = $this->getOptions($state);
6175
if (!$this->statement instanceof Result) {
6276
$this->statement = $this->initializeStatement($state);
@@ -102,6 +116,7 @@ public function finalize(ProcessState $state): void
102116

103117
protected function initializeStatement(ProcessState $state): Result
104118
{
119+
/** @var Options $options */
105120
$options = $this->getOptions($state);
106121
$connection = $this->getConnection($state);
107122
$sql = $options['sql'];
@@ -121,7 +136,10 @@ protected function initializeStatement(ProcessState $state): Result
121136

122137
$sql = $qb->getSQL();
123138
}
124-
$params = $options['input_as_params'] ? $state->getInput() : $options['params'];
139+
140+
/** @var array<string> $inputAsParams */
141+
$inputAsParams = $state->getInput();
142+
$params = $options['input_as_params'] ? $inputAsParams : $options['params'];
125143

126144
return $connection->executeQuery($sql, $params, $options['types']);
127145
}
@@ -168,7 +186,9 @@ protected function configureOptions(OptionsResolver $resolver): void
168186

169187
protected function getConnection(ProcessState $state): Connection
170188
{
171-
/* @noinspection PhpIncompatibleReturnTypeInspection */
172-
return $this->doctrine->getConnection($this->getOption($state, 'connection'));
189+
/** @var Connection $connection */
190+
$connection = $this->doctrine->getConnection($this->getOption($state, 'connection'));
191+
192+
return $connection;
173193
}
174194
}

src/Task/Database/DatabaseUpdaterTask.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use CleverAge\ProcessBundle\Model\ProcessState;
1818
use Doctrine\DBAL\Connection;
1919
use Doctrine\DBAL\Exception;
20+
use Doctrine\DBAL\Types\Type;
2021
use Doctrine\Persistence\ManagerRegistry;
2122
use Psr\Log\LoggerInterface;
2223
use Symfony\Component\OptionsResolver\OptionsResolver;
@@ -25,6 +26,13 @@
2526
* Execute an update/delete in the database from a SQL statement.
2627
*
2728
* @see https://www.doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion
29+
*
30+
* @phpstan-type Options array{
31+
* 'sql': string,
32+
* 'input_as_params': bool,
33+
* 'params': mixed,
34+
* 'types': array<int, int|string|Type|null>|array<string, int|string|Type|null>
35+
* }
2836
*/
2937
class DatabaseUpdaterTask extends AbstractConfigurableTask
3038
{
@@ -48,15 +56,18 @@ public function execute(ProcessState $state): void
4856
*/
4957
protected function initializeStatement(ProcessState $state): int
5058
{
59+
/** @var Options $options */
5160
$options = $this->getOptions($state);
5261
$connection = $this->getConnection($state);
5362

54-
$params = $options['input_as_params'] ? $state->getInput() : $options['params'];
63+
/** @var array<string> $inputAsParams */
64+
$inputAsParams = $state->getInput();
65+
$params = $options['input_as_params'] ? $inputAsParams : $options['params'];
5566
if (!\is_array($params)) {
5667
throw new \UnexpectedValueException('Expecting an array of params');
5768
}
5869

59-
return $connection->executeStatement($options['sql'], $params, $options['types']);
70+
return (int) $connection->executeStatement($options['sql'], $params, $options['types']);
6071
}
6172

6273
protected function configureOptions(OptionsResolver $resolver): void
@@ -77,7 +88,9 @@ protected function configureOptions(OptionsResolver $resolver): void
7788

7889
protected function getConnection(ProcessState $state): Connection
7990
{
80-
/* @noinspection PhpIncompatibleReturnTypeInspection */
81-
return $this->doctrine->getConnection($this->getOption($state, 'connection'));
91+
/** @var Connection $connection */
92+
$connection = $this->doctrine->getConnection($this->getOption($state, 'connection'));
93+
94+
return $connection;
8295
}
8396
}

src/Task/EntityManager/AbstractDoctrineQueryTask.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@
2020

2121
/**
2222
* Easily extendable task to query entities in their repository.
23+
*
24+
* @phpstan-type Options array{
25+
* 'class_name': class-string,
26+
* 'criteria': array<string, string|array<string|int>|null>,
27+
* 'order_by': array<string, string|null>,
28+
* 'limit': ?int,
29+
* 'offset': ?int,
30+
* 'empty_log_level': string,
31+
* }
2332
*/
2433
abstract class AbstractDoctrineQueryTask extends AbstractDoctrineTask
2534
{
@@ -56,6 +65,13 @@ protected function configureOptions(OptionsResolver $resolver): void
5665
);
5766
}
5867

68+
/**
69+
* @template TEntityClass of object
70+
*
71+
* @param EntityRepository<TEntityClass> $repository
72+
* @param array<string, string|array<string|int>|null> $criteria
73+
* @param array<string, string|null> $orderBy
74+
*/
5975
protected function getQueryBuilder(
6076
EntityRepository $repository,
6177
array $criteria,
@@ -80,7 +96,6 @@ protected function getQueryBuilder(
8096
$qb->setParameter($parameterName, $value);
8197
}
8298
}
83-
/* @noinspection ForeachSourceInspection */
8499
foreach ($orderBy as $field => $order) {
85100
$qb->addOrderBy("e.{$field}", $order);
86101
}

src/Task/EntityManager/DoctrineBatchWriterTask.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
class DoctrineBatchWriterTask extends AbstractDoctrineTask implements FlushableTaskInterface
2626
{
27+
/** @var array<object> */
2728
protected array $batch = [];
2829

2930
public function flush(ProcessState $state): void
@@ -60,9 +61,9 @@ protected function writeBatch(ProcessState $state): void
6061
}
6162

6263
// Support for multiple entity managers is overkill but might be necessary
64+
/** @var \SplObjectStorage<EntityManagerInterface, null> $entityManagers */
6365
$entityManagers = new \SplObjectStorage();
6466
foreach ($this->batch as $entity) {
65-
/** @var object $entity */
6667
$class = ClassUtils::getClass($entity);
6768
$entityManager = $this->doctrine->getManagerForClass($class);
6869
if (!$entityManager instanceof EntityManagerInterface) {

src/Task/EntityManager/DoctrineReaderTask.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222

2323
/**
2424
* Fetch entities from doctrine.
25+
*
26+
* @phpstan-import-type Options from AbstractDoctrineQueryTask
2527
*/
2628
class DoctrineReaderTask extends AbstractDoctrineQueryTask implements IterableTaskInterface
2729
{
28-
protected ?\IteratorIterator $iterator = null;
30+
protected ?\Iterator $iterator = null;
2931

3032
public function __construct(
3133
protected LoggerInterface $logger,
@@ -41,7 +43,7 @@ public function __construct(
4143
*/
4244
public function next(ProcessState $state): bool
4345
{
44-
if (!$this->iterator instanceof \IteratorIterator) {
46+
if (!$this->iterator instanceof \Iterator) {
4547
return false;
4648
}
4749
$this->iterator->next();
@@ -51,8 +53,9 @@ public function next(ProcessState $state): bool
5153

5254
public function execute(ProcessState $state): void
5355
{
56+
/** @var Options $options */
5457
$options = $this->getOptions($state);
55-
if (!$this->iterator instanceof \IteratorIterator) {
58+
if (!$this->iterator instanceof \Iterator) {
5659
/** @var class-string $class */
5760
$class = $options['class_name'];
5861
$entityManager = $this->doctrine->getManagerForClass($class);
@@ -62,10 +65,12 @@ public function execute(ProcessState $state): void
6265
$repository = $entityManager->getRepository($class);
6366
$this->initIterator($repository, $options);
6467
}
65-
$result = $this->iterator->current();
68+
if ($this->iterator instanceof \Iterator) {
69+
$result = $this->iterator->current();
70+
}
6671

6772
// Handle empty results
68-
if (false === $result) {
73+
if (!isset($result) || false === $result) {
6974
$logContext = [
7075
'options' => $options,
7176
];
@@ -79,6 +84,12 @@ public function execute(ProcessState $state): void
7984
$state->setOutput($result);
8085
}
8186

87+
/**
88+
* @template TEntityClass of object
89+
*
90+
* @param EntityRepository<TEntityClass> $repository
91+
* @param Options $options
92+
*/
8293
protected function initIterator(EntityRepository $repository, array $options): void
8394
{
8495
$qb = $this->getQueryBuilder(
@@ -89,7 +100,7 @@ protected function initIterator(EntityRepository $repository, array $options): v
89100
$options['offset']
90101
);
91102

92-
$this->iterator = new \IteratorIterator($qb->getQuery()->toIterable());
103+
$this->iterator = new \ArrayIterator(iterator_to_array($qb->getQuery()->toIterable()));
93104
$this->iterator->rewind();
94105
}
95106
}

src/Task/EntityManager/DoctrineWriterTask.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
use CleverAge\ProcessBundle\Model\ProcessState;
1717
use Doctrine\Common\Util\ClassUtils;
18-
use Doctrine\ORM\EntityManager;
1918
use Doctrine\ORM\EntityManagerInterface;
2019

2120
/**
@@ -31,14 +30,13 @@ public function execute(ProcessState $state): void
3130
protected function writeEntity(ProcessState $state): mixed
3231
{
3332
$this->getOptions($state);
34-
/** @var object $entity */
33+
/** @var ?object $entity */
3534
$entity = $state->getInput();
3635

3736
if (null === $entity) {
3837
throw new \RuntimeException('DoctrineWriterTask does not allow null input');
3938
}
4039
$class = ClassUtils::getClass($entity);
41-
/** @var ?EntityManager $entityManager */
4240
$entityManager = $this->doctrine->getManagerForClass($class);
4341
if (!$entityManager instanceof EntityManagerInterface) {
4442
throw new \UnexpectedValueException("No manager found for class {$class}");

0 commit comments

Comments
 (0)