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
3 changes: 1 addition & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ test-resources export-ignore
.github export-ignore
.gitattributes export-ignore
.gitignore export-ignore
.phpstorm.meta.php export-ignore
CHANGELOG.md export-ignore
docker-compose.yml export-ignore
Dockerfile export-ignore
indocker export-ignore
phpcs.xml export-ignore
mago.toml export-ignore
phpstan.neon export-ignore
phpunit.xml.dist export-ignore
shlink-installer.gif export-ignore
23 changes: 0 additions & 23 deletions .phpstorm.meta.php

This file was deleted.

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).

## [10.1.0] - 2026-06-11
### Added
* *Nothing*

### Changed
* Migrate form PHP_CodeSniffer to Mago for code linting and formatting.

### Deprecated
* *Nothing*

### Removed
* *Nothing*

### Fixed
* *Nothing*


## [10.0.0] - 2026-02-08
### Added
* Add support for Symfony 8.0
Expand Down
12 changes: 8 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@
"webimpress/safe-writer": "^2.2"
},
"require-dev": {
"carthage-software/mago": "^1.28",
"devster/ubench": "^2.1",
"phpstan/phpstan": "^2.1",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^13.0",
"roave/security-advisories": "dev-master",
"shlinkio/php-coding-standard": "~2.5.0",
"shlinkio/shlink-mago-config": "^1.0",
"symfony/var-dumper": "^8.0"
},
"autoload": {
Expand All @@ -47,8 +47,12 @@
"@stan",
"@test:ci"
],
"cs": "phpcs",
"cs:fix": "phpcbf",
"lint": "mago lint",
"lint:fix": "mago lint --fix",
"format": "mago format",
"format:check": "mago format --check",
"cs": ["@lint", "@format:check"],
"cs:fix": ["@lint:fix", "@format"],
"stan": "phpstan analyse",
"test": "phpunit --order-by=random --testdox --testdox-summary",
"test:ci": [
Expand Down
30 changes: 14 additions & 16 deletions config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
use Symfony\Component\Process\PhpExecutableFinder;

return [

'dependencies' => [
'factories' => [
Console\Application::class => Factory\ApplicationFactory::class,
Expand Down Expand Up @@ -47,32 +46,32 @@
'DATABASE' => [
'Database > Driver' => Config\Option\Database\DatabaseDriverConfigOption::class,
'Database > Name' => Config\Option\Database\DatabaseNameConfigOption::class,
'Database > Host (or unix socket for PostgreSQL)'
=> Config\Option\Database\DatabaseHostConfigOption::class,
'Database > Host (or unix socket for PostgreSQL)' =>
Config\Option\Database\DatabaseHostConfigOption::class,
'Database > Port' => Config\Option\Database\DatabasePortConfigOption::class,
'Database > User' => Config\Option\Database\DatabaseUserConfigOption::class,
'Database > Password' => Config\Option\Database\DatabasePasswordConfigOption::class,
'Database > Unix socket (Mysql/MariaDB)'
=> Config\Option\Database\DatabaseUnixSocketConfigOption::class,
'Database > Unix socket (Mysql/MariaDB)' =>
Config\Option\Database\DatabaseUnixSocketConfigOption::class,
'Database > Use encryption' => Config\Option\Database\DatabaseUseEncryptionConfigOption::class,
],
'URL SHORTENER' => [
'URL shortener > Short domain' => Config\Option\UrlShortener\ShortDomainHostConfigOption::class,
'URL shortener > Schema' => Config\Option\UrlShortener\ShortDomainSchemaConfigOption::class,
'URL shortener > Short codes length' => Config\Option\UrlShortener\ShortCodeLengthOption::class,
'URL shortener > Auto resolve titles'
=> Config\Option\UrlShortener\AutoResolveTitlesConfigOption::class,
'URL shortener > Auto resolve titles' =>
Config\Option\UrlShortener\AutoResolveTitlesConfigOption::class,
'URL shortener > Extra path mode' => Config\Option\UrlShortener\ExtraPathModeConfigOption::class,
'URL shortener > Multi-segment slugs'
=> Config\Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
'URL shortener > Multi-segment slugs' =>
Config\Option\UrlShortener\EnableMultiSegmentSlugsConfigOption::class,
'URL shortener > Trailing slashes' => Config\Option\UrlShortener\EnableTrailingSlashConfigOption::class,
'URL shortener > Mode' => Config\Option\UrlShortener\ShortUrlModeConfigOption::class,
'GeoLite2 license key' => Config\Option\UrlShortener\GeoLiteLicenseKeyConfigOption::class,
'Redirects > Status code (301/302/307/308)'
=> Config\Option\UrlShortener\RedirectStatusCodeConfigOption::class,
'Redirects > Status code (301/302/307/308)' =>
Config\Option\UrlShortener\RedirectStatusCodeConfigOption::class,
'Redirects > Caching life time' => Config\Option\UrlShortener\RedirectCacheLifeTimeConfigOption::class,
'Redirects > Caching visibility'
=> Config\Option\UrlShortener\RedirectCacheVisibilityConfigOption::class,
'Redirects > Caching visibility' =>
Config\Option\UrlShortener\RedirectCacheVisibilityConfigOption::class,
],
'TRACKING' => [
'Tracking > Orphan visits tracking' => Config\Option\Tracking\OrphanVisitsTrackingConfigOption::class,
Expand All @@ -82,8 +81,8 @@
'Tracking > Disable IP address tracking' => Config\Option\Tracking\DisableIpTrackingConfigOption::class,
'Tracking > IP Anonymization' => Config\Option\Tracking\IpAnonymizationConfigOption::class,
'Tracking > Disable user agent tracking' => Config\Option\Tracking\DisableUaTrackingConfigOption::class,
'Tracking > Disable referrer tracking'
=> Config\Option\Tracking\DisableReferrerTrackingConfigOption::class,
'Tracking > Disable referrer tracking' =>
Config\Option\Tracking\DisableReferrerTrackingConfigOption::class,
],
'REDIRECTS' => [
'Redirects > Base URL' => Config\Option\Redirect\BaseUrlRedirectConfigOption::class,
Expand Down Expand Up @@ -300,5 +299,4 @@
],
],
],

];
10 changes: 5 additions & 5 deletions config/container.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

$shlinkConfigLoader = static function () {
$appConfigPath = __DIR__ . '/../../../../config/config.php';
if (! file_exists($appConfigPath)) {
if (!file_exists($appConfigPath)) {
return [];
}

Expand All @@ -35,12 +35,12 @@
return $appConfig;
};

$config = (new ConfigAggregator([
$config = new ConfigAggregator([
Config\ConfigProvider::class,
new PhpFileProvider(__DIR__ . '/config.php'), // Installer config
$shlinkConfigLoader, // Overwritten config coming from Shlink
new PhpFileProvider(__DIR__ . '/config.php'), // Installer config
$shlinkConfigLoader, // Overwritten config coming from Shlink
new PhpFileProvider(__DIR__ . '/config.local.php'), // Local config
]))->getMergedConfig();
])->getMergedConfig();

$container = new ServiceManager($config['dependencies']);
$container->setService('config', $config);
Expand Down
19 changes: 19 additions & 0 deletions mago.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#:schema https://mago.carthage.software/1.27.1/schema.json
# Welcome to Mago!
# For full documentation, see https://mago.carthage.software/tools/overview
version = "1"
php-version = "8.4.0"
extends = "vendor/shlinkio/shlink-mago-config/mago.toml"

[source]
workspace = "."
paths = ["config", "src", "test"]
excludes = ["vendor"]

[source.glob]
literal-separator = true

[linter.rules]
no-literal-password = { exclude = ["test"] }
no-empty-catch-clause = { exclude = ["test"] }
no-empty-loop = { exclude = ["test"] }
20 changes: 0 additions & 20 deletions phpcs.xml

This file was deleted.

33 changes: 21 additions & 12 deletions src/Command/InitCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#[AsCommand(
name: InitCommand::NAME,
description: 'Initializes external dependencies required for Shlink to properly work, like DB, cache warmup, '
. 'initial GeoLite DB download, etc',
. 'initial GeoLite DB download, etc',
)]
class InitCommand extends Command
{
Expand All @@ -33,16 +33,25 @@ public function __invoke(SymfonyStyle $io, InputInterface $input, #[MapInput] In
{
$commands = [...$inputData->resolveCommands()];

return array_reduce($commands, function (bool $carry, array $commandInfo) use ($input, $io): bool {
/** @var array{InstallationCommand, string|null} $commandInfo */
[$command, $arg] = $commandInfo;

return $this->commandsRunner->execPhpCommand(
name: $command->value,
io: $io,
interactive: $input->isInteractive(),
args: $arg !== null ? [$arg] : [],
) && $carry;
}, initial: true) ? self::SUCCESS : self::FAILURE;
return array_reduce(
$commands,
function (bool $carry, array $commandInfo) use ($input, $io): bool {
/** @var array{InstallationCommand, string|null} $commandInfo */
[$command, $arg] = $commandInfo;

return (
$this->commandsRunner->execPhpCommand(
name: $command->value,
io: $io,
interactive: $input->isInteractive(),
args: $arg !== null ? [$arg] : [],
)
&& $carry
);
},
initial: true,
)
? self::SUCCESS
: self::FAILURE;
}
}
4 changes: 2 additions & 2 deletions src/Command/Model/InitCommandInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ final class InitCommandInput
*/
public function resolveCommands(): iterable
{
if (! $this->skipInitializeDb) {
if (!$this->skipInitializeDb) {
yield [InstallationCommand::DB_CREATE_SCHEMA, null];
}

Expand All @@ -53,7 +53,7 @@ public function resolveCommands(): iterable
yield [InstallationCommand::ORM_CLEAR_CACHE, null];
}

if (! $this->skipDownloadGeolite) {
if (!$this->skipDownloadGeolite) {
yield [InstallationCommand::GEOLITE_DOWNLOAD_DB, null];
}

Expand Down
2 changes: 2 additions & 0 deletions src/Command/Model/InitOption.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?php

declare(strict_types=1);

namespace Shlinkio\Shlink\Installer\Command\Model;

/** @deprecated */
Expand Down
7 changes: 4 additions & 3 deletions src/Command/SetOptionCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public function __construct(
parent::__construct();
$this->groups = array_filter(
iterator_to_array($this->flattenGroupsWithTitle($groups)),
static fn (string $configOption) => $enabledOptions === null || ArrayUtils::contains(
static fn (string $configOption) => $enabledOptions === null
|| ArrayUtils::contains(
$configOption,
$enabledOptions,
),
Expand All @@ -56,15 +57,15 @@ private function flattenGroupsWithTitle(iterable $groups): Generator
foreach ($groups as $key => $value) {
if (is_iterable($value)) {
yield from $this->flattenGroupsWithTitle($value);
} elseif (! is_numeric($key)) {
} elseif (!is_numeric($key)) {
yield $key => $value;
}
}
}

public function __invoke(SymfonyStyle $io): int
{
if (! $this->filesystem->exists($this->generatedConfigPath)) {
if (!$this->filesystem->exists($this->generatedConfigPath)) {
throw InvalidShlinkPathException::forCurrentPath();
}

Expand Down
26 changes: 16 additions & 10 deletions src/Config/ConfigGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ public function __construct(
private readonly ConfigOptionsManagerInterface $configOptionsManager,
private readonly array $configOptionsGroups,
private readonly array|null $enabledOptions,
) {
}
) {}

public function generateConfigInteractively(StyleInterface $io, array $previousConfig): array
{
Expand All @@ -33,12 +32,13 @@ public function generateConfigInteractively(StyleInterface $io, array $previousC
// FIXME Improve code quality on these nested loops
foreach ($pluginsGroups as $title => $configOptions) {
foreach ($configOptions as $configOption => $plugin) {
$optionIsEnabled = $this->enabledOptions === null || ArrayUtils::contains(
$optionIsEnabled = $this->enabledOptions === null
|| ArrayUtils::contains(
$configOption,
$this->enabledOptions,
);
$shouldAsk = $optionIsEnabled && $plugin->shouldBeAsked($answers);
if (! $shouldAsk) {
if (!$shouldAsk) {
if ($plugin instanceof ConfigOptionMigratorInterface && isset($answers[$plugin->getEnvVar()])) {
$answers[$plugin->getEnvVar()] = $plugin->tryToMigrateValue($answers[$plugin->getEnvVar()]);
}
Expand All @@ -47,7 +47,7 @@ public function generateConfigInteractively(StyleInterface $io, array $previousC
}

// Render every title only once, and only as soon as we find a plugin that should be asked
if (! ArrayUtils::contains($title, $alreadyRenderedTitles)) {
if (!ArrayUtils::contains($title, $alreadyRenderedTitles)) {
$alreadyRenderedTitles[] = $title;
$io->title($title);
}
Expand All @@ -73,17 +73,23 @@ private function resolveAndSortOptions(): array
// Sort plugins based on which other plugins they depend on
usort(
$plugins,
static fn (ConfigOptionInterface $a, ConfigOptionInterface $b): int =>
$a instanceof DependentConfigOptionInterface && $a->getDependentOption() === $b::class ? 1 : 0,
static fn (ConfigOptionInterface $a, ConfigOptionInterface $b): int => $a
instanceof DependentConfigOptionInterface
&& $a->getDependentOption() === $b::class
? 1
: 0,
);

return array_combine($configOptions, $plugins);
};
$filterDisabledOptions = fn (array $configOptions) => array_filter(
$configOptions,
fn (string $option) => $this->enabledOptions === null || ArrayUtils::contains(
$option,
$this->enabledOptions,
fn (string $option) => (
$this->enabledOptions === null
|| ArrayUtils::contains(
$option,
$this->enabledOptions,
)
),
);

Expand Down
Loading
Loading