Skip to content

Commit

Permalink
#11 Push to phpstan level 8
Browse files Browse the repository at this point in the history
  • Loading branch information
xmarchegay authored and njoubert-cleverage committed Dec 13, 2024
1 parent 7fb7c40 commit 82fe912
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 33 deletions.
15 changes: 1 addition & 14 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,18 +1,5 @@
parameters:
level: 6
level: 8
paths:
- src
- tests
ignoreErrors:
- '#type has no value type specified in iterable type#'
- '#has parameter .* with no value type specified in iterable type#'
- '#has no value type specified in iterable type array#'
- '#configureOptions\(\) has no return type specified.#'
- '#configure\(\) has no return type specified#'
- '#process\(\) has no return type specified#'
- '#should return Iterator but returns Traversable#'
- '#Negated boolean expression is always false#'
checkGenericClassInNonGenericObjectType: false
reportUnmatchedIgnoredErrors: false
inferPrivatePropertyTypeFromConstructor: true
treatPhpDocTypesAsCertain: false
28 changes: 24 additions & 4 deletions src/Task/Database/DatabaseReaderTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,26 @@
use CleverAge\ProcessBundle\Model\ProcessState;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Result;
use Doctrine\DBAL\Types\Type;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Symfony\Component\OptionsResolver\OptionsResolver;

/**
* Fetch entities from doctrine.
*
* @phpstan-type Options array{
* 'sql': ?string,
* 'table': string,
* 'limit': ?int,
* 'empty_log_level': string,
* 'paginate': ?int,
* 'offset': ?int,
* 'input_as_params': bool,
* 'params': array<int<0, max>|string, mixed>,
* 'types': array<int, int|string|Type|null>|array<string, int|string|Type|null>
* }
*/
class DatabaseReaderTask extends AbstractConfigurableTask implements IterableTaskInterface, FinalizableTaskInterface
{
Expand All @@ -42,7 +55,7 @@ public function __construct(
/**
* Moves the internal pointer to the next element,
* return true if the task has a next element
* return false if the task has terminated it's iteration.
* return false if the task has terminated its iteration.
*/
public function next(ProcessState $state): bool
{
Expand All @@ -57,6 +70,7 @@ public function next(ProcessState $state): bool

public function execute(ProcessState $state): void
{
/** @var Options $options */
$options = $this->getOptions($state);
if (!$this->statement instanceof Result) {
$this->statement = $this->initializeStatement($state);
Expand Down Expand Up @@ -102,6 +116,7 @@ public function finalize(ProcessState $state): void

protected function initializeStatement(ProcessState $state): Result
{
/** @var Options $options */
$options = $this->getOptions($state);
$connection = $this->getConnection($state);
$sql = $options['sql'];
Expand All @@ -121,7 +136,10 @@ protected function initializeStatement(ProcessState $state): Result

$sql = $qb->getSQL();
}
$params = $options['input_as_params'] ? $state->getInput() : $options['params'];

/** @var array<string> $inputAsParams */
$inputAsParams = $state->getInput();
$params = $options['input_as_params'] ? $inputAsParams : $options['params'];

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

protected function getConnection(ProcessState $state): Connection
{
/* @noinspection PhpIncompatibleReturnTypeInspection */
return $this->doctrine->getConnection($this->getOption($state, 'connection'));
/** @var Connection $connection */
$connection = $this->doctrine->getConnection($this->getOption($state, 'connection'));

return $connection;
}
}
21 changes: 17 additions & 4 deletions src/Task/Database/DatabaseUpdaterTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use CleverAge\ProcessBundle\Model\ProcessState;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Exception;
use Doctrine\DBAL\Types\Type;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Log\LoggerInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
Expand All @@ -25,6 +26,13 @@
* Execute an update/delete in the database from a SQL statement.
*
* @see https://www.doctrine-project.org/projects/doctrine-dbal/en/2.9/reference/data-retrieval-and-manipulation.html#list-of-parameters-conversion
*
* @phpstan-type Options array{
* 'sql': string,
* 'input_as_params': bool,
* 'params': mixed,
* 'types': array<int, int|string|Type|null>|array<string, int|string|Type|null>
* }
*/
class DatabaseUpdaterTask extends AbstractConfigurableTask
{
Expand All @@ -48,15 +56,18 @@ public function execute(ProcessState $state): void
*/
protected function initializeStatement(ProcessState $state): int
{
/** @var Options $options */
$options = $this->getOptions($state);
$connection = $this->getConnection($state);

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

return $connection->executeStatement($options['sql'], $params, $options['types']);
return (int) $connection->executeStatement($options['sql'], $params, $options['types']);
}

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

protected function getConnection(ProcessState $state): Connection
{
/* @noinspection PhpIncompatibleReturnTypeInspection */
return $this->doctrine->getConnection($this->getOption($state, 'connection'));
/** @var Connection $connection */
$connection = $this->doctrine->getConnection($this->getOption($state, 'connection'));

return $connection;
}
}
17 changes: 16 additions & 1 deletion src/Task/EntityManager/AbstractDoctrineQueryTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@

/**
* Easily extendable task to query entities in their repository.
*
* @phpstan-type Options array{
* 'class_name': class-string,
* 'criteria': array<string, string|array<string|int>|null>,
* 'order_by': array<string, string|null>,
* 'limit': ?int,
* 'offset': ?int,
* 'empty_log_level': string,
* }
*/
abstract class AbstractDoctrineQueryTask extends AbstractDoctrineTask
{
Expand Down Expand Up @@ -56,6 +65,13 @@ protected function configureOptions(OptionsResolver $resolver): void
);
}

/**
* @template TEntityClass of object
*
* @param EntityRepository<TEntityClass> $repository
* @param array<string, string|array<string|int>|null> $criteria
* @param array<string, string|null> $orderBy
*/
protected function getQueryBuilder(
EntityRepository $repository,
array $criteria,
Expand All @@ -80,7 +96,6 @@ protected function getQueryBuilder(
$qb->setParameter($parameterName, $value);
}
}
/* @noinspection ForeachSourceInspection */
foreach ($orderBy as $field => $order) {
$qb->addOrderBy("e.{$field}", $order);
}
Expand Down
3 changes: 2 additions & 1 deletion src/Task/EntityManager/DoctrineBatchWriterTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
class DoctrineBatchWriterTask extends AbstractDoctrineTask implements FlushableTaskInterface
{
/** @var array<object> */
protected array $batch = [];

public function flush(ProcessState $state): void
Expand Down Expand Up @@ -60,9 +61,9 @@ protected function writeBatch(ProcessState $state): void
}

// Support for multiple entity managers is overkill but might be necessary
/** @var \SplObjectStorage<EntityManagerInterface, null> $entityManagers */
$entityManagers = new \SplObjectStorage();
foreach ($this->batch as $entity) {
/** @var object $entity */
$class = ClassUtils::getClass($entity);
$entityManager = $this->doctrine->getManagerForClass($class);
if (!$entityManager instanceof EntityManagerInterface) {
Expand Down
23 changes: 17 additions & 6 deletions src/Task/EntityManager/DoctrineReaderTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,12 @@

/**
* Fetch entities from doctrine.
*
* @phpstan-import-type Options from AbstractDoctrineQueryTask
*/
class DoctrineReaderTask extends AbstractDoctrineQueryTask implements IterableTaskInterface
{
protected ?\IteratorIterator $iterator = null;
protected ?\Iterator $iterator = null;

public function __construct(
protected LoggerInterface $logger,
Expand All @@ -41,7 +43,7 @@ public function __construct(
*/
public function next(ProcessState $state): bool
{
if (!$this->iterator instanceof \IteratorIterator) {
if (!$this->iterator instanceof \Iterator) {
return false;
}
$this->iterator->next();
Expand All @@ -51,8 +53,9 @@ public function next(ProcessState $state): bool

public function execute(ProcessState $state): void
{
/** @var Options $options */
$options = $this->getOptions($state);
if (!$this->iterator instanceof \IteratorIterator) {
if (!$this->iterator instanceof \Iterator) {
/** @var class-string $class */
$class = $options['class_name'];
$entityManager = $this->doctrine->getManagerForClass($class);
Expand All @@ -62,10 +65,12 @@ public function execute(ProcessState $state): void
$repository = $entityManager->getRepository($class);
$this->initIterator($repository, $options);
}
$result = $this->iterator->current();
if ($this->iterator instanceof \Iterator) {
$result = $this->iterator->current();
}

// Handle empty results
if (false === $result) {
if (!isset($result) || false === $result) {
$logContext = [
'options' => $options,
];
Expand All @@ -79,6 +84,12 @@ public function execute(ProcessState $state): void
$state->setOutput($result);
}

/**
* @template TEntityClass of object
*
* @param EntityRepository<TEntityClass> $repository
* @param Options $options
*/
protected function initIterator(EntityRepository $repository, array $options): void
{
$qb = $this->getQueryBuilder(
Expand All @@ -89,7 +100,7 @@ protected function initIterator(EntityRepository $repository, array $options): v
$options['offset']
);

$this->iterator = new \IteratorIterator($qb->getQuery()->toIterable());
$this->iterator = new \ArrayIterator(iterator_to_array($qb->getQuery()->toIterable()));
$this->iterator->rewind();
}
}
4 changes: 1 addition & 3 deletions src/Task/EntityManager/DoctrineWriterTask.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

use CleverAge\ProcessBundle\Model\ProcessState;
use Doctrine\Common\Util\ClassUtils;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;

/**
Expand All @@ -31,14 +30,13 @@ public function execute(ProcessState $state): void
protected function writeEntity(ProcessState $state): mixed
{
$this->getOptions($state);
/** @var object $entity */
/** @var ?object $entity */
$entity = $state->getInput();

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

0 comments on commit 82fe912

Please sign in to comment.