diff --git a/psalm.xml.dist b/psalm.xml.dist index a7ac0ef..8b144b4 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -13,7 +13,15 @@ > <issueHandlers> <!-- - Psalm doesn't recognize some variables always being set because of prior validation. + This is a false-positive caused by Doctrine's EntityManagerDecorator. + --> + <MethodSignatureMismatch> + <errorLevel type="suppress"> + <file name="src/EntityManager.php"/> + </errorLevel> + </MethodSignatureMismatch> + <!-- + This is a false-positive caused by Doctrine's ArrayCollection. --> <PossiblyNullReference> <errorLevel type="suppress"> @@ -30,15 +38,6 @@ --> <PropertyNotSetInConstructor errorLevel="suppress"/> <RedundantPropertyInitializationCheck errorLevel="suppress"/> - <!-- - We deliberately want to evaluate empty strings as FALSE in those files. - --> - <RiskyTruthyFalsyComparison> - <errorLevel type="suppress"> - <file name="src/Console/AddRecordCommand.php"/> - <file name="src/Console/AddSetCommand.php"/> - </errorLevel> - </RiskyTruthyFalsyComparison> <!-- Those classes are dynamically used depending on the given OAI verb. @see src/Middleware/Dispatcher.php:95 diff --git a/src/Console.php b/src/Console.php index 786a940..490e316 100644 --- a/src/Console.php +++ b/src/Console.php @@ -107,12 +107,12 @@ abstract class Console extends Command protected function getPhpMemoryLimit(): int { if (!isset($this->memoryLimit)) { - $ini = trim(ini_get('memory_limit')); - $limit = (int) $ini; - if ($limit < 0) { + $phpValue = (string) ini_get('memory_limit'); + $limit = (int) $phpValue; + if ($limit <= 0) { return -1; } - $unit = strtolower($ini[strlen($ini) - 1]); + $unit = strtolower($phpValue[strlen($phpValue) - 1]); switch ($unit) { case 'g': $limit *= 1024; diff --git a/src/Console/AddRecordCommand.php b/src/Console/AddRecordCommand.php index eee5280..b29a0db 100644 --- a/src/Console/AddRecordCommand.php +++ b/src/Console/AddRecordCommand.php @@ -42,13 +42,14 @@ use Symfony\Component\Console\Output\OutputInterface; name: 'oai:records:add', description: 'Add or update a record in the database' )] -class AddRecordCommand extends Console +final class AddRecordCommand extends Console { /** * Configures the current command. * * @return void */ + #[\Override] protected function configure(): void { $this->addArgument( @@ -82,6 +83,7 @@ class AddRecordCommand extends Console * * @return int 0 if everything went fine, or an error code */ + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { if (!$this->validateInput($input, $output)) { @@ -90,7 +92,7 @@ class AddRecordCommand extends Console /** @var Format */ $format = $this->em->getMetadataFormat($this->arguments['format']); - $content = file_get_contents($this->arguments['file']) ?: ''; + $content = (string) file_get_contents($this->arguments['file']); $record = new Record($this->arguments['identifier'], $format); if (trim($content) !== '') { diff --git a/src/Console/AddSetCommand.php b/src/Console/AddSetCommand.php index 27b0c53..ac64f62 100644 --- a/src/Console/AddSetCommand.php +++ b/src/Console/AddSetCommand.php @@ -40,13 +40,14 @@ use Symfony\Component\Console\Output\OutputInterface; name: 'oai:sets:add', description: 'Add or update a set in the database' )] -class AddSetCommand extends Console +final class AddSetCommand extends Console { /** * Configures the current command. * * @return void */ + #[\Override] protected function configure(): void { $this->addArgument( @@ -79,6 +80,7 @@ class AddSetCommand extends Console * * @return int 0 if everything went fine, or an error code */ + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { if (!$this->validateInput($input, $output)) { @@ -86,6 +88,7 @@ class AddSetCommand extends Console } if (array_key_exists('file', $this->arguments)) { + /** @psalm-suppress RiskyTruthyFalsyComparison */ $description = file_get_contents($this->arguments['file']) ?: null; } diff --git a/src/Console/CsvImportCommand.php b/src/Console/CsvImportCommand.php index f66f7fb..2d6aecf 100644 --- a/src/Console/CsvImportCommand.php +++ b/src/Console/CsvImportCommand.php @@ -52,13 +52,14 @@ use Symfony\Component\Console\Output\OutputInterface; name: 'oai:records:import:csv', description: 'Import records from a CSV file' )] -class CsvImportCommand extends Console +final class CsvImportCommand extends Console { /** * Configures the current command. * * @return void */ + #[\Override] protected function configure(): void { $this->addArgument( @@ -120,6 +121,7 @@ class CsvImportCommand extends Console * * @return int 0 if everything went fine, or an error code */ + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { if (!$this->validateInput($input, $output)) { @@ -226,6 +228,7 @@ class CsvImportCommand extends Console $headers = array_flip($headers); $callback = function (string $column) use ($headers): ?int { + /** @psalm-suppress InvalidArgument */ return array_key_exists($column, $headers) ? $headers[$column] : null; }; diff --git a/src/Console/DeleteRecordCommand.php b/src/Console/DeleteRecordCommand.php index ab411a3..388089a 100644 --- a/src/Console/DeleteRecordCommand.php +++ b/src/Console/DeleteRecordCommand.php @@ -39,13 +39,14 @@ use Symfony\Component\Console\Output\OutputInterface; name: 'oai:records:delete', description: 'Delete a record while obeying deleted record policy' )] -class DeleteRecordCommand extends Console +final class DeleteRecordCommand extends Console { /** * Configures the current command. * * @return void */ + #[\Override] protected function configure(): void { $this->addArgument( @@ -69,6 +70,7 @@ class DeleteRecordCommand extends Console * * @return int 0 if everything went fine, or an error code */ + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { if (!$this->validateInput($input, $output)) { diff --git a/src/Console/PruneDeletedRecordsCommand.php b/src/Console/PruneDeletedRecordsCommand.php index 5385936..b485e04 100644 --- a/src/Console/PruneDeletedRecordsCommand.php +++ b/src/Console/PruneDeletedRecordsCommand.php @@ -40,13 +40,14 @@ use Symfony\Component\Console\Output\OutputInterface; name: 'oai:records:prune', description: 'Prune deleted records from database' )] -class PruneDeletedRecordsCommand extends Console +final class PruneDeletedRecordsCommand extends Console { /** * Configures the current command. * * @return void */ + #[\Override] protected function configure(): void { $this->addOption( @@ -66,13 +67,14 @@ class PruneDeletedRecordsCommand extends Console * * @return int 0 if everything went fine, or an error code */ + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { $policy = Configuration::getInstance()->deletedRecords; - $forced = (bool) $input->getOption('force'); + $forced = $input->getOption('force'); if ( $policy === 'no' - or ($policy === 'transient' && $forced) + or ($policy === 'transient' && $forced === true) ) { $deleted = $this->em->pruneDeletedRecords(); $this->clearResultCache(); diff --git a/src/Console/PruneResumptionTokensCommand.php b/src/Console/PruneResumptionTokensCommand.php index 302ab03..74cec76 100644 --- a/src/Console/PruneResumptionTokensCommand.php +++ b/src/Console/PruneResumptionTokensCommand.php @@ -38,7 +38,7 @@ use Symfony\Component\Console\Output\OutputInterface; name: 'oai:tokens:prune', description: 'Prune expired resumption tokens from database' )] -class PruneResumptionTokensCommand extends Console +final class PruneResumptionTokensCommand extends Console { /** * Executes the current command. @@ -48,6 +48,7 @@ class PruneResumptionTokensCommand extends Console * * @return int 0 if everything went fine, or an error code */ + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { $expired = $this->em->pruneExpiredTokens(); diff --git a/src/Console/UpdateFormatsCommand.php b/src/Console/UpdateFormatsCommand.php index d617e65..1dc0952 100644 --- a/src/Console/UpdateFormatsCommand.php +++ b/src/Console/UpdateFormatsCommand.php @@ -41,7 +41,7 @@ use Symfony\Component\Validator\Exception\ValidationFailedException; name: 'oai:formats:update', description: 'Update metadata formats in database from configuration' )] -class UpdateFormatsCommand extends Console +final class UpdateFormatsCommand extends Console { /** * Executes the current command. @@ -51,6 +51,7 @@ class UpdateFormatsCommand extends Console * * @return int 0 if everything went fine, or an error code */ + #[\Override] protected function execute(InputInterface $input, OutputInterface $output): int { $formats = Configuration::getInstance()->metadataPrefix; diff --git a/src/Entity/Format.php b/src/Entity/Format.php index f749b7f..d58451b 100644 --- a/src/Entity/Format.php +++ b/src/Entity/Format.php @@ -37,7 +37,7 @@ use Symfony\Component\Validator\Exception\ValidationFailedException; */ #[ORM\Entity(repositoryClass: FormatRepository::class)] #[ORM\Table(name: 'formats')] -class Format extends Entity +final class Format extends Entity { /** * The unique metadata prefix. diff --git a/src/Entity/Record.php b/src/Entity/Record.php index a8d20ed..640703e 100644 --- a/src/Entity/Record.php +++ b/src/Entity/Record.php @@ -42,7 +42,7 @@ use Symfony\Component\Validator\Exception\ValidationFailedException; #[ORM\Index(name: 'format_idx', columns: ['format'])] #[ORM\Index(name: 'last_changed_idx', columns: ['last_changed'])] #[ORM\Index(name: 'format_last_changed_idx', columns: ['format', 'last_changed'])] -class Record extends Entity +final class Record extends Entity { /** * The record identifier. diff --git a/src/Entity/Set.php b/src/Entity/Set.php index b051d3f..3d2d3b3 100644 --- a/src/Entity/Set.php +++ b/src/Entity/Set.php @@ -37,7 +37,7 @@ use Symfony\Component\Validator\Exception\ValidationFailedException; */ #[ORM\Entity(repositoryClass: SetRepository::class)] #[ORM\Table(name: 'sets')] -class Set extends Entity +final class Set extends Entity { /** * The unique set spec. diff --git a/src/Entity/Token.php b/src/Entity/Token.php index 1a09ac9..ef21c00 100644 --- a/src/Entity/Token.php +++ b/src/Entity/Token.php @@ -40,7 +40,7 @@ use OCC\OaiPmh2\Repository\TokenRepository; #[ORM\Entity(repositoryClass: TokenRepository::class)] #[ORM\Table(name: 'tokens')] #[ORM\Index(name: 'valid_until_idx', columns: ['valid_until'])] -class Token extends Entity +final class Token extends Entity { /** * The resumption token. diff --git a/src/EntityManager.php b/src/EntityManager.php index 14e659f..56f992a 100644 --- a/src/EntityManager.php +++ b/src/EntityManager.php @@ -205,11 +205,11 @@ final class EntityManager extends EntityManagerDecorator ->setMaxResults($maxRecords); if (isset($from)) { $dql->andWhere($dql->expr()->gte('records.lastChanged', ':from')); - $dql->setParameter('from', new DateTime($from)); + $dql->setParameter('from', new DateTime($from), 'datetime'); } if (isset($until)) { $dql->andWhere($dql->expr()->lte('records.lastChanged', ':until')); - $dql->setParameter('until', new DateTime($until)); + $dql->setParameter('until', new DateTime($until), 'datetime'); } if (isset($set)) { $dql->innerJoin( @@ -338,7 +338,7 @@ final class EntityManager extends EntityManagerDecorator public function isValidRecordIdentifier(string $identifier): bool { $records = $this->getRepository(Record::class)->findBy(['identifier' => $identifier]); - return (bool) count($records) > 0; + return count($records) > 0; } /** @@ -369,7 +369,7 @@ final class EntityManager extends EntityManagerDecorator $dql = $this->createQueryBuilder(); $dql->delete(Token::class, 'tokens') ->where($dql->expr()->lt('tokens.validUntil', ':now')) - ->setParameter('now', new DateTime()); + ->setParameter('now', new DateTime(), 'datetime'); /** @var int */ return $dql->getQuery()->execute(); } diff --git a/src/Middleware.php b/src/Middleware.php index bc8dbfc..c65ecec 100644 --- a/src/Middleware.php +++ b/src/Middleware.php @@ -142,6 +142,7 @@ abstract class Middleware extends AbstractMiddleware * * @return ServerRequestInterface The processed server request */ + #[\Override] protected function processRequest(ServerRequestInterface $request): ServerRequestInterface { /** @var OaiRequestMetadata */ @@ -158,6 +159,7 @@ abstract class Middleware extends AbstractMiddleware * * @return ResponseInterface The processed response */ + #[\Override] protected function processResponse(ResponseInterface $response): ResponseInterface { if (!ErrorHandler::getInstance()->hasErrors() && isset($this->preparedResponse)) { diff --git a/src/Middleware/Dispatcher.php b/src/Middleware/Dispatcher.php index 84858f3..0bfd431 100644 --- a/src/Middleware/Dispatcher.php +++ b/src/Middleware/Dispatcher.php @@ -34,7 +34,7 @@ use Psr\Http\Message\ServerRequestInterface; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class Dispatcher extends AbstractMiddleware +final class Dispatcher extends AbstractMiddleware { /** * List of defined OAI-PMH parameters. @@ -85,6 +85,7 @@ class Dispatcher extends AbstractMiddleware * * @return ServerRequestInterface The processed server request */ + #[\Override] protected function processRequest(ServerRequestInterface $request): ServerRequestInterface { $request = $this->getRequestWithAttributes($request); @@ -108,6 +109,7 @@ class Dispatcher extends AbstractMiddleware * * @return ResponseInterface The final response */ + #[\Override] protected function processResponse(ResponseInterface $response): ResponseInterface { // TODO: Add support for content compression diff --git a/src/Middleware/ErrorHandler.php b/src/Middleware/ErrorHandler.php index c4e51f6..9fa9764 100644 --- a/src/Middleware/ErrorHandler.php +++ b/src/Middleware/ErrorHandler.php @@ -36,7 +36,7 @@ use Psr\Http\Message\StreamInterface; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class ErrorHandler extends AbstractMiddleware +final class ErrorHandler extends AbstractMiddleware { use Singleton; @@ -99,6 +99,7 @@ class ErrorHandler extends AbstractMiddleware * * @return ResponseInterface The error response */ + #[\Override] protected function processResponse(ResponseInterface $response): ResponseInterface { if ($this->hasErrors()) { diff --git a/src/Middleware/GetRecord.php b/src/Middleware/GetRecord.php index 53ea423..e98a3e1 100644 --- a/src/Middleware/GetRecord.php +++ b/src/Middleware/GetRecord.php @@ -34,7 +34,7 @@ use Psr\Http\Message\ServerRequestInterface; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class GetRecord extends Middleware +final class GetRecord extends Middleware { /** * Prepare the response body for verb "GetRecord". @@ -43,6 +43,7 @@ class GetRecord extends Middleware * * @return void */ + #[\Override] protected function prepareResponse(ServerRequestInterface $request): void { $oaiRecord = $this->em->getRecord( diff --git a/src/Middleware/Identify.php b/src/Middleware/Identify.php index f2e9fd5..a02e399 100644 --- a/src/Middleware/Identify.php +++ b/src/Middleware/Identify.php @@ -36,7 +36,7 @@ use Psr\Http\Message\ServerRequestInterface; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class Identify extends Middleware +final class Identify extends Middleware { /** * Prepare the response body for verb "Identify". @@ -45,6 +45,7 @@ class Identify extends Middleware * * @return void */ + #[\Override] protected function prepareResponse(ServerRequestInterface $request): void { $response = new Response($request); diff --git a/src/Middleware/ListIdentifiers.php b/src/Middleware/ListIdentifiers.php index 4fa6b07..de3724d 100644 --- a/src/Middleware/ListIdentifiers.php +++ b/src/Middleware/ListIdentifiers.php @@ -43,6 +43,7 @@ class ListIdentifiers extends Middleware * * @return void */ + #[\Override] protected function prepareResponse(ServerRequestInterface $request): void { $this->checkResumptionToken(); diff --git a/src/Middleware/ListMetadataFormats.php b/src/Middleware/ListMetadataFormats.php index d32277f..3fca10f 100644 --- a/src/Middleware/ListMetadataFormats.php +++ b/src/Middleware/ListMetadataFormats.php @@ -34,7 +34,7 @@ use Psr\Http\Message\ServerRequestInterface; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class ListMetadataFormats extends Middleware +final class ListMetadataFormats extends Middleware { /** * Prepare the response body for verb "ListMetadataFormats". @@ -43,6 +43,7 @@ class ListMetadataFormats extends Middleware * * @return void */ + #[\Override] protected function prepareResponse(ServerRequestInterface $request): void { $formats = $this->em->getMetadataFormats($this->arguments['identifier']); diff --git a/src/Middleware/ListRecords.php b/src/Middleware/ListRecords.php index 1e601f2..55691f0 100644 --- a/src/Middleware/ListRecords.php +++ b/src/Middleware/ListRecords.php @@ -30,7 +30,7 @@ namespace OCC\OaiPmh2\Middleware; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class ListRecords extends ListIdentifiers +final class ListRecords extends ListIdentifiers { /** * "ListIdentifiers" and "ListRecords" are practically identical except the diff --git a/src/Middleware/ListSets.php b/src/Middleware/ListSets.php index 18f9b6b..4c66a98 100644 --- a/src/Middleware/ListSets.php +++ b/src/Middleware/ListSets.php @@ -34,7 +34,7 @@ use Psr\Http\Message\ServerRequestInterface; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class ListSets extends Middleware +final class ListSets extends Middleware { /** * Prepare the response body for verb "ListSets". @@ -43,6 +43,7 @@ class ListSets extends Middleware * * @return void */ + #[\Override] protected function prepareResponse(ServerRequestInterface $request): void { $this->checkResumptionToken(); diff --git a/src/Validator/ConfigurationValidator.php b/src/Validator/ConfigurationValidator.php index 39f4484..6925e5d 100644 --- a/src/Validator/ConfigurationValidator.php +++ b/src/Validator/ConfigurationValidator.php @@ -33,7 +33,7 @@ use Symfony\Component\Validator\Validation; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class ConfigurationValidator +final class ConfigurationValidator { /** * Get constraints for configuration array. diff --git a/src/Validator/RegExValidator.php b/src/Validator/RegExValidator.php index 2516758..ad4e7b0 100644 --- a/src/Validator/RegExValidator.php +++ b/src/Validator/RegExValidator.php @@ -33,7 +33,7 @@ use Symfony\Component\Validator\Validation; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class RegExValidator +final class RegExValidator { /** * Get constraints for regular expression. diff --git a/src/Validator/UrlValidator.php b/src/Validator/UrlValidator.php index a05437d..cc4897e 100644 --- a/src/Validator/UrlValidator.php +++ b/src/Validator/UrlValidator.php @@ -33,7 +33,7 @@ use Symfony\Component\Validator\Validation; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class UrlValidator +final class UrlValidator { /** * Get constraints for URLs. diff --git a/src/Validator/XmlValidator.php b/src/Validator/XmlValidator.php index 42046c5..653adf9 100644 --- a/src/Validator/XmlValidator.php +++ b/src/Validator/XmlValidator.php @@ -34,7 +34,7 @@ use Symfony\Component\Validator\Validation; * @author Sebastian Meyer <sebastian.meyer@opencultureconsulting.com> * @package OAIPMH2 */ -class XmlValidator +final class XmlValidator { /** * Get constraints for XML.