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
2 changes: 1 addition & 1 deletion .github/actions/ci-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ runs:
coverage: xdebug
- name: Install dependencies
if: ${{ inputs.install-deps == 'yes' }}
run: composer install --no-interaction --prefer-dist
run: composer install --no-interaction --prefer-dist ${{ inputs.php-version == '8.6' && '--ignore-platform-req=php' || '' }}
shell: bash
3 changes: 2 additions & 1 deletion .github/workflows/ci-db-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ jobs:
runs-on: ubuntu-24.04
strategy:
matrix:
php-version: ['8.4', '8.5']
php-version: ['8.4', '8.5', '8.6']
env:
LC_ALL: C
continue-on-error: ${{ matrix.php-version == '8.6' }}
steps:
- uses: actions/checkout@v5
- name: Install MSSQL ODBC
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ jobs:
runs-on: ubuntu-24.04
strategy:
matrix:
php-version: ['8.4', '8.5']
php-version: ['8.4', '8.5', '8.6']
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # rr get-binary picks this env automatically
continue-on-error: ${{ matrix.php-version == '8.6' }}
steps:
- uses: actions/checkout@v5
- name: Start postgres database server
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ The only exception is when a major version has just been released and the securi

## Reporting a Vulnerability

Do not report potential vulnerabilities as public issues. Instead, do it through https://github.com/shlinkio/shlink/security
Do not report potential vulnerabilities as public issues. ~Instead, do it through https://github.com/shlinkio/shlink/security~ Every single report this project has received was AI slop, so I have disabled the capability.
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"cuyz/valinor": "~2.5.0",
"doctrine/dbal": "^4.4",
"doctrine/migrations": "^3.9",
"doctrine/orm": "^3.6",
"doctrine/orm": "^3.7",
"donatj/phpuseragentparser": "^1.11",
"friendsofphp/proxy-manager-lts": "^1.0",
"geoip2/geoip2": "^3.3",
Expand All @@ -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",
Expand Down
100 changes: 2 additions & 98 deletions mago-analyze-baseline.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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<array-key, string>`, but provided type `array<array-key, mixed>` 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<array-key, string>|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<array-key, string>|string`.'
count = 1

[[issues]]
file = "module/CLI/src/Util/ShlinkTable.php"
code = "mixed-assignment"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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<array-key, string>|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]]
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -1806,12 +1734,6 @@ code = "less-specific-nested-return-statement"
message = '''Returned type `array<array-key, mixed>` is less specific than the declared return type `array<array-key, Shlinkio\Shlink\Core\Visit\Entity\Visit>` 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"
Expand All @@ -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"
Expand Down Expand Up @@ -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<int, Shlinkio\Shlink\Core\RedirectRule\Entity\RedirectCondition>`, but provided type `Doctrine\Common\Collections\ArrayCollection<array-key, mixed>` 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"
Expand Down
3 changes: 3 additions & 0 deletions module/CLI/src/Util/PhpProcessRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
16 changes: 13 additions & 3 deletions module/CLI/src/Util/ProcessRunner.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,30 @@
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;

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
? $createProcess(...)
: static fn (array $cmd) => new Process($cmd, timeout: self::TIMEOUT);
}

/**
* @inheritDoc
*/
public function run(OutputInterface $output, array $cmd): void
{
if ($output instanceof ConsoleOutputInterface) {
Expand All @@ -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()),
),
);
}

Expand All @@ -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()));
}
}
}
3 changes: 3 additions & 0 deletions module/CLI/src/Util/ProcessRunnerInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,8 @@

interface ProcessRunnerInterface
{
/**
* @param string[] $cmd
*/
public function run(OutputInterface $output, array $cmd): void;
}
3 changes: 2 additions & 1 deletion module/Core/src/ShortUrl/Entity/ShortUrl.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\Common\Collections\Order;
use Doctrine\Common\Collections\Selectable;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
use Shlinkio\Shlink\Core\Domain\Entity\Domain;
Expand Down Expand Up @@ -186,7 +187,7 @@ public function mostRecentImportedVisitDate(): Chronos|null
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq('type', VisitType::IMPORTED))
->orderBy(['id' => 'DESC'])
->orderBy(['id' => Order::Descending])
->setMaxResults(1);
$visit = $this->visits->matching($criteria)->last();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
);
}

Expand Down
13 changes: 7 additions & 6 deletions module/Core/src/ShortUrl/Persistence/ShortUrlsCountFiltering.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading