diff --git a/CHANGELOG.md b/CHANGELOG.md index 6630cf606..403bb86c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org). +## [5.1.7] - 2026-09-21 +### Added +* *Nothing* + +### Changed +* *Nothing* + +### Deprecated +* *Nothing* + +### Removed +* *Nothing* + +### Fixed +* [#2658](https://github.com/shlinkio/shlink/issues/2658) Fix redis connections left open sometimes. + + ## [5.1.6] - 2026-09-06 ### Added * *Nothing* diff --git a/composer.json b/composer.json index 231189f7e..e4e952276 100644 --- a/composer.json +++ b/composer.json @@ -42,7 +42,7 @@ "pagerfanta/core": "^3.8", "ramsey/uuid": "^4.9", "shlinkio/doctrine-specification": "^2.3", - "shlinkio/shlink-common": "^9.0.1", + "shlinkio/shlink-common": "^9.0.2", "shlinkio/shlink-config": "^4.1.0", "shlinkio/shlink-event-dispatcher": "^4.4.0", "shlinkio/shlink-importer": "^5.8", diff --git a/mago-analyze-baseline.toml b/mago-analyze-baseline.toml index c0594ae87..6149e14b2 100644 --- a/mago-analyze-baseline.toml +++ b/mago-analyze-baseline.toml @@ -450,54 +450,6 @@ code = "less-specific-nested-argument-type" message = 'Argument type mismatch for argument #1 of `Symfony\Component\Process\Process::__construct`: expected `array`, but provided type `array` is less specific.' count = 1 -[[issues]] -file = "module/CLI/src/Util/ProcessRunner.php" -code = "mixed-argument" -message = "Invalid argument type for argument #1 of `spl_object_hash`: expected `object`, but found `mixed`." -count = 2 - -[[issues]] -file = "module/CLI/src/Util/ProcessRunner.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #2 of `Symfony\Component\Console\Helper\ProcessHelper::wrapCallback`: expected `Symfony\Component\Process\Process`, but found `mixed`.' -count = 1 - -[[issues]] -file = "module/CLI/src/Util/ProcessRunner.php" -code = "mixed-argument" -message = "Invalid argument type for argument #2 of `sprintf`: expected `Stringable|null|scalar`, but found `mixed`." -count = 1 - -[[issues]] -file = "module/CLI/src/Util/ProcessRunner.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #3 of `Symfony\Component\Console\Helper\DebugFormatterHelper::stop`: expected `bool`, but found `mixed`.' -count = 1 - -[[issues]] -file = "module/CLI/src/Util/ProcessRunner.php" -code = "mixed-argument" -message = "Invalid argument type for argument #3 of `str_replace`: expected `array|string`, but found `mixed`." -count = 1 - -[[issues]] -file = "module/CLI/src/Util/ProcessRunner.php" -code = "mixed-assignment" -message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 1 - -[[issues]] -file = "module/CLI/src/Util/ProcessRunner.php" -code = "mixed-method-access" -message = "Attempting to access a method on a non-object type (`mixed`)." -count = 5 - -[[issues]] -file = "module/CLI/src/Util/ProcessRunner.php" -code = "possibly-invalid-argument" -message = 'Possible argument type mismatch for argument #2 of `Symfony\Component\Console\Helper\DebugFormatterHelper::start`: expected `string`, but possibly received `array|string`.' -count = 1 - [[issues]] file = "module/CLI/src/Util/ShlinkTable.php" code = "mixed-assignment" @@ -642,12 +594,6 @@ code = "mixed-argument" message = "Invalid argument type for argument #6 of `sprintf`: expected `Stringable|null|scalar`, but found `mixed`." count = 1 -[[issues]] -file = "module/Core/functions/functions.php" -code = "mixed-array-access" -message = "Unsafe array access on type `nonnull`." -count = 1 - [[issues]] file = "module/Core/functions/functions.php" code = "mixed-array-assignment" @@ -723,7 +669,7 @@ count = 1 [[issues]] file = "module/Core/functions/functions.php" code = "possibly-null-argument" -message = "Argument #3 of function `preg_replace` is possibly `null`, but parameter type `array|string` does not accept it." +message = "Argument #3 of function `preg_replace` is possibly `null`, but parameter type `array<('K.preg_replace() extends array-key), string>|string` does not accept it." count = 1 [[issues]] @@ -1098,17 +1044,11 @@ code = "mixed-array-access" message = "Unsafe array access on type `mixed`." count = 1 -[[issues]] -file = "module/Core/src/Config/PostProcessor/ShortUrlMethodsProcessor.php" -code = "mixed-array-assignment" -message = "Unsafe array assignment on type `nonnull`." -count = 1 - [[issues]] file = "module/Core/src/Config/PostProcessor/ShortUrlMethodsProcessor.php" code = "mixed-assignment" message = "Assigning `mixed` type to a variable may lead to unexpected behavior." -count = 2 +count = 1 [[issues]] file = "module/Core/src/Config/PostProcessor/ShortUrlMethodsProcessor.php" @@ -1452,12 +1392,6 @@ code = "possibly-null-argument" message = 'Argument #2 of method `Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryTrait::applySpecification` is possibly `null`, but parameter type `Happyr\DoctrineSpecification\Filter\Filter|Happyr\DoctrineSpecification\Query\QueryModifier` does not accept it.' count = 1 -[[issues]] -file = "module/Core/src/ShortUrl/Repository/ShortUrlListRepository.php" -code = "possibly-null-property-access" -message = "Attempting to access a property on a possibly `null` value." -count = 2 - [[issues]] file = "module/Core/src/ShortUrl/Repository/ShortUrlRepository.php" code = "mixed-assignment" @@ -1764,12 +1698,6 @@ code = "mixed-method-access" message = "Attempting to access a method on a non-object type (`mixed`)." count = 1 -[[issues]] -file = "module/Core/src/Visit/Repository/VisitIterationRepository.php" -code = "possibly-null-property-access" -message = "Attempting to access a property on a possibly `null` value." -count = 2 - [[issues]] file = "module/Core/src/Visit/Repository/VisitRepository.php" code = "array-to-string-conversion" @@ -1806,12 +1734,6 @@ code = "less-specific-nested-return-statement" message = '''Returned type `array` is less specific than the declared return type `array` for function `Shlinkio\Shlink\Core\Visit\Repository\VisitRepository::findVisitsByTag` due to nested 'mixed'.''' count = 1 -[[issues]] -file = "module/Core/src/Visit/Repository/VisitRepository.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `Doctrine\DBAL\Connection::quote`: expected `string`, but found `mixed`.' -count = 2 - [[issues]] file = "module/Core/src/Visit/Repository/VisitRepository.php" code = "mixed-return-statement" @@ -1824,24 +1746,12 @@ code = "mixed-return-statement" message = 'Could not infer a precise return type for function `Shlinkio\Shlink\Core\Visit\Repository\VisitRepository::resolveVisitsWithNativeQuery`. Saw type `mixed`.' count = 1 -[[issues]] -file = "module/Core/src/Visit/Repository/VisitRepository.php" -code = "possible-method-access-on-null" -message = "Attempting to call a method on `null`." -count = 2 - [[issues]] file = "module/Core/src/Visit/Repository/VisitRepository.php" code = "possibly-null-argument" message = 'Argument #2 of method `Happyr\DoctrineSpecification\Repository\EntitySpecificationRepositoryTrait::applySpecification` is possibly `null`, but parameter type `Happyr\DoctrineSpecification\Filter\Filter|Happyr\DoctrineSpecification\Query\QueryModifier` does not accept it.' count = 3 -[[issues]] -file = "module/Core/src/Visit/Repository/VisitRepository.php" -code = "possibly-null-property-access" -message = "Attempting to access a property on a possibly `null` value." -count = 2 - [[issues]] file = "module/Core/src/Visit/RequestTracker.php" code = "mixed-assignment" @@ -2118,12 +2028,6 @@ code = "less-specific-argument" message = 'Argument type mismatch for argument #1 of `ShlinkioTest\Shlink\Core\RedirectRule\Entity\ShortUrlRedirectRuleTest::createRule`: expected `Doctrine\Common\Collections\ArrayCollection`, but provided type `Doctrine\Common\Collections\ArrayCollection` is less specific.' count = 1 -[[issues]] -file = "module/Core/test/ShortUrl/Entity/ShortUrlTest.php" -code = "possibly-invalid-argument" -message = 'Possible argument type mismatch for argument #1 of `PHPUnit\Framework\Assert::assertStringStartsWith`: expected `non-empty-string`, but possibly received `string`.' -count = 1 - [[issues]] file = "module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php" code = "redundant-comparison" diff --git a/module/CLI/src/Util/PhpProcessRunner.php b/module/CLI/src/Util/PhpProcessRunner.php index 47f8161c1..537570492 100644 --- a/module/CLI/src/Util/PhpProcessRunner.php +++ b/module/CLI/src/Util/PhpProcessRunner.php @@ -19,6 +19,9 @@ public function __construct(private ProcessRunnerInterface $wrappedProcessRunner $this->phpBinary = $phpFinder->find(includeArgs: false) ?: 'php'; } + /** + * @inheritDoc + */ public function run(OutputInterface $output, array $cmd): void { $this->wrappedProcessRunner->run($output, [$this->phpBinary, ...$cmd]); diff --git a/module/CLI/src/Util/ProcessRunner.php b/module/CLI/src/Util/ProcessRunner.php index a27efa605..8c35e77bb 100644 --- a/module/CLI/src/Util/ProcessRunner.php +++ b/module/CLI/src/Util/ProcessRunner.php @@ -11,7 +11,7 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Process; -use function spl_object_hash; +use function spl_object_id; use function sprintf; use function str_replace; @@ -19,8 +19,12 @@ class ProcessRunner implements ProcessRunnerInterface { private const int TIMEOUT = 1_200; // 20 minutes + /** @var Closure(string[] $cmd): Process */ private Closure $createProcess; + /** + * @param null|(callable(string[] $cmd): Process) $createProcess + */ public function __construct(private readonly ProcessHelper $helper, callable|null $createProcess = null) { $this->createProcess = $createProcess !== null @@ -28,6 +32,9 @@ public function __construct(private readonly ProcessHelper $helper, callable|nul : static fn (array $cmd) => new Process($cmd, timeout: self::TIMEOUT); } + /** + * @inheritDoc + */ public function run(OutputInterface $output, array $cmd): void { if ($output instanceof ConsoleOutputInterface) { @@ -40,7 +47,10 @@ public function run(OutputInterface $output, array $cmd): void if ($output->isVeryVerbose()) { $output->write( - $formatter->start(spl_object_hash($process), str_replace('<', '\\<', $process->getCommandLine())), + $formatter->start( + (string) spl_object_id($process), + str_replace('<', '\\<', $process->getCommandLine()), + ), ); } @@ -54,7 +64,7 @@ public function run(OutputInterface $output, array $cmd): void '%s Command did not run successfully', $process->getExitCode(), ); - $output->write($formatter->stop(spl_object_hash($process), $message, $process->isSuccessful())); + $output->write($formatter->stop((string) spl_object_id($process), $message, $process->isSuccessful())); } } } diff --git a/module/CLI/src/Util/ProcessRunnerInterface.php b/module/CLI/src/Util/ProcessRunnerInterface.php index c00a4691d..d3c5ec916 100644 --- a/module/CLI/src/Util/ProcessRunnerInterface.php +++ b/module/CLI/src/Util/ProcessRunnerInterface.php @@ -8,5 +8,8 @@ interface ProcessRunnerInterface { + /** + * @param string[] $cmd + */ public function run(OutputInterface $output, array $cmd): void; } diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php b/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php index 160cd20d0..0dfcaf1ed 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php @@ -117,10 +117,12 @@ private function tryToResolveTitle(ResponseInterface $response, string $contentT } return ( - $this->encodeToUtf8WithMbString($titleInOriginalEncoding, $pageCharset) ?? $this->encodeToUtf8WithIconv( + $this->encodeToUtf8WithMbString($titleInOriginalEncoding, $pageCharset) + ?? $this->encodeToUtf8WithIconv( $titleInOriginalEncoding, $pageCharset, - ) ?? $titleInOriginalEncoding + ) + ?? $titleInOriginalEncoding ); } diff --git a/module/Core/src/ShortUrl/Persistence/ShortUrlsCountFiltering.php b/module/Core/src/ShortUrl/Persistence/ShortUrlsCountFiltering.php index 9386b791b..dad17dc85 100644 --- a/module/Core/src/ShortUrl/Persistence/ShortUrlsCountFiltering.php +++ b/module/Core/src/ShortUrl/Persistence/ShortUrlsCountFiltering.php @@ -34,12 +34,13 @@ public function __construct( public readonly TagsMode $excludeTagsMode = TagsMode::ANY, string|null $apiKeyName = null, ) { - $this->searchIncludesDefaultDomain = !empty($searchTerm) - && !empty($defaultDomain) - && str_contains( - strtolower($defaultDomain), - strtolower($searchTerm), - ); + $this->searchIncludesDefaultDomain = + !empty($searchTerm) + && !empty($defaultDomain) + && str_contains( + strtolower($defaultDomain), + strtolower($searchTerm), + ); // Filtering by API key name is only allowed if the API key used in the request is an admin one, or it matches // the API key name