diff --git a/.dockerignore b/.dockerignore index e3aff686c..dff3cea10 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,6 @@ bin/rr config/autoload/*local* +config/params/shlink_dev_env.* data/infra data/cache/* data/log/* diff --git a/.gitattributes b/.gitattributes index 4d66fe589..a34ddbf9b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,7 +13,6 @@ .travis.yml export-ignore build.sh export-ignore CHANGELOG.md export-ignore -docker-compose.override.yml.dist export-ignore docker-compose.yml export-ignore indocker export-ignore phpcs.xml export-ignore diff --git a/.github/actions/ci-setup/action.yml b/.github/actions/ci-setup/action.yml index f800cf097..37ec30dfe 100644 --- a/.github/actions/ci-setup/action.yml +++ b/.github/actions/ci-setup/action.yml @@ -40,8 +40,7 @@ runs: php-version: ${{ inputs.php-version }} tools: composer extensions: ${{ inputs.php-extensions }} - coverage: pcov - ini-values: pcov.directory=module + coverage: xdebug - name: Install dependencies if: ${{ inputs.install-deps == 'yes' }} run: composer install --no-interaction --prefer-dist ${{ inputs.php-version == '8.4' && '--ignore-platform-req=php' || '' }} diff --git a/.github/workflows/ci-docker-image-build.yml b/.github/workflows/ci-docker-image-build.yml index ab9681c50..b2fe8b54e 100644 --- a/.github/workflows/ci-docker-image-build.yml +++ b/.github/workflows/ci-docker-image-build.yml @@ -1,4 +1,4 @@ -name: Build docker image +name: Test docker image build on: pull_request: @@ -7,8 +7,4 @@ on: jobs: build-docker-image: - runs-on: ubuntu-24.04 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - run: docker build -t shlink-docker-image:temp . + uses: shlinkio/github-actions/.github/workflows/docker-image-build-ci.yml@main diff --git a/.gitignore b/.gitignore index a7f9b895b..9353d40c9 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,5 @@ data/GeoLite2-City.* data/infra/matomo docs/swagger-ui* docs/mercure.html -docker-compose.override.yml .phpunit.result.cache docs/swagger/swagger-inlined.json -phpcov* diff --git a/CHANGELOG.md b/CHANGELOG.md index 7575178bc..82e3d4e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,24 @@ 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). +## [4.2.4] - 2024-10-27 +### Added +* *Nothing* + +### Changed +* [#2231](https://github.com/shlinkio/shlink/issues/2231) Update to `endroid/qr-code` 6.0. +* [#2221](https://github.com/shlinkio/shlink/issues/2221) Switch to env vars to handle dev/local options. + +### Deprecated +* *Nothing* + +### Removed +* *Nothing* + +### Fixed +* [#2232](https://github.com/shlinkio/shlink/issues/2232) Run RoadRunner in docker with `exec` to ensure signals are properly handled. + + ## [4.2.3] - 2024-10-17 ### Added * *Nothing* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ee94c70e..daf34a63a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,11 +16,14 @@ The first thing you need to do is fork the repository, and clone it in your loca Then you will have to follow these steps: -* Copy all files with `.local.php.dist` extension from `config/autoload` by removing the dist extension. +* Copy the `config/params/shlink_dev_env.php.dist` in the same directory, but removing the `.dist` extension: - For example the `common.local.php.dist` file should be copied as `common.local.php`. + ``` + cp config/params/shlink_dev_env.php.dist config/params/shlink_dev_env.php + ``` + + The `shlink_dev_env.php` file is gitignored, so you can customize it as you want. For example, by adding your own GeoLite license key. -* Copy the file `docker-compose.override.yml.dist` by also removing the `dist` extension. * Start-up the project by running `docker compose up`. The first time this command is run, it will create several containers that are used during development, so it may take some time. diff --git a/Dockerfile b/Dockerfile index 4251b3e41..e6e94734e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,7 +43,7 @@ RUN apk add --no-cache git && \ php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \ php composer.phar clear-cache && \ rm -r docker composer.* && \ - sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php + sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" module/Core/src/Config/Options/AppOptions.php # Prepare final image @@ -61,7 +61,6 @@ EXPOSE 8080 # Copy config specific for the image COPY docker/docker-entrypoint.sh docker-entrypoint.sh -COPY docker/config/shlink_in_docker.local.php config/autoload/shlink_in_docker.local.php COPY docker/config/php.ini ${PHP_INI_DIR}/conf.d/ USER ${USER_ID} diff --git a/README.md b/README.md index 7f8cc1641..77cbaa43c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ [![Mastodon](https://img.shields.io/mastodon/follow/109329425426175098?color=%236364ff&domain=https%3A%2F%2Ffosstodon.org&label=follow&logo=mastodon&logoColor=white&style=flat-square)](https://fosstodon.org/@shlinkio) [![Bluesky](https://img.shields.io/badge/follow-shlinkio-0285FF.svg?style=flat-square&logo=bluesky&logoColor=white)](https://bsky.app/profile/shlinkio.bsky.social) -[![Twitter](https://img.shields.io/badge/follow-shlinkio-blue.svg?style=flat-square&logo=x&color=black)](https://twitter.com/shlinkio) [![Paypal donate](https://img.shields.io/badge/Donate-paypal-blue.svg?style=flat-square&logo=paypal&colorA=aaaaaa)](https://slnk.to/donate) A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own domain. diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index b96cf6719..ffc152b7e 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -6,6 +6,8 @@ export TEST_RUNTIME="${TEST_RUNTIME:-"rr"}" # rr is the only runtime currently s export DB_DRIVER="${DB_DRIVER:-"postgres"}" export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}" +[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage + # Reset logs OUTPUT_LOGS=data/log/api-tests/output.log rm -rf data/log/api-tests diff --git a/bin/test/run-cli-tests.sh b/bin/test/run-cli-tests.sh new file mode 100755 index 000000000..60b97fccd --- /dev/null +++ b/bin/test/run-cli-tests.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env sh + +export APP_ENV=test +export TEST_ENV=cli +export DB_DRIVER="${DB_DRIVER:-"maria"}" +export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}" + +[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage + +vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-cli.xml $* +TESTS_EXIT_CODE=$? + +# Exit this script with the same code as the tests. If tests failed, this script has to fail +exit $TESTS_EXIT_CODE diff --git a/build.sh b/build.sh index 7b77295fc..6786c492e 100755 --- a/build.sh +++ b/build.sh @@ -35,8 +35,8 @@ ${composerBin} install --no-dev --prefer-dist --optimize-autoloader --no-progres echo 'Deleting dev files...' rm composer.* -# Update Shlink version in config -sed -i "s/%SHLINK_VERSION%/${version}/g" config/autoload/app_options.global.php +# Update Shlink version +sed -i "s/%SHLINK_VERSION%/${version}/g" module/Core/src/Config/Options/AppOptions.php # Compressing file echo 'Compressing files...' diff --git a/composer.json b/composer.json index 8c85e1eba..498724572 100644 --- a/composer.json +++ b/composer.json @@ -18,64 +18,64 @@ "ext-json": "*", "ext-mbstring": "*", "ext-pdo": "*", - "akrabat/ip-address-middleware": "^2.1", - "cakephp/chronos": "^3.0.2", - "doctrine/dbal": "^4.1", - "doctrine/migrations": "^3.6", - "doctrine/orm": "^3.2", - "endroid/qr-code": "^5.0", + "akrabat/ip-address-middleware": "^2.3", + "cakephp/chronos": "^3.1", + "doctrine/dbal": "^4.2", + "doctrine/migrations": "^3.8", + "doctrine/orm": "^3.3", + "endroid/qr-code": "^6.0", "friendsofphp/proxy-manager-lts": "^1.0", "geoip2/geoip2": "^3.0", - "guzzlehttp/guzzle": "^7.5", + "guzzlehttp/guzzle": "^7.9", "hidehalo/nanoid-php": "^1.1", "jaybizzle/crawler-detect": "^1.2.116", - "laminas/laminas-config": "^3.8", + "laminas/laminas-config": "^3.9", "laminas/laminas-config-aggregator": "^1.15", - "laminas/laminas-diactoros": "^3.3", - "laminas/laminas-inputfilter": "^2.27", - "laminas/laminas-servicemanager": "^3.21", - "laminas/laminas-stdlib": "^3.17", - "matomo/matomo-php-tracker": "^3.2", - "mezzio/mezzio": "^3.17", - "mezzio/mezzio-fastroute": "^3.11", - "mezzio/mezzio-problem-details": "^1.13", + "laminas/laminas-diactoros": "^3.5", + "laminas/laminas-inputfilter": "^2.30", + "laminas/laminas-servicemanager": "^3.22", + "laminas/laminas-stdlib": "^3.19", + "matomo/matomo-php-tracker": "^3.3", + "mezzio/mezzio": "^3.20", + "mezzio/mezzio-fastroute": "^3.12", + "mezzio/mezzio-problem-details": "^1.15", "mlocati/ip-lib": "^1.18", "mobiledetect/mobiledetectlib": "^4.8", "pagerfanta/core": "^3.8", "ramsey/uuid": "^4.7", "shlinkio/doctrine-specification": "^2.1.1", - "shlinkio/shlink-common": "^6.3", - "shlinkio/shlink-config": "^3.2.1", + "shlinkio/shlink-common": "^6.4", + "shlinkio/shlink-config": "^3.3", "shlinkio/shlink-event-dispatcher": "^4.1", "shlinkio/shlink-importer": "^5.3.2", "shlinkio/shlink-installer": "^9.2", - "shlinkio/shlink-ip-geolocation": "^4.0", + "shlinkio/shlink-ip-geolocation": "^4.1", "shlinkio/shlink-json": "^1.1", "spiral/roadrunner": "^2024.1", "spiral/roadrunner-cli": "^2.6", "spiral/roadrunner-http": "^3.5", "spiral/roadrunner-jobs": "^4.5", - "symfony/console": "^7.0", - "symfony/filesystem": "^7.0", - "symfony/lock": "^7.0", - "symfony/process": "^7.0", - "symfony/string": "^7.0" + "symfony/console": "^7.1", + "symfony/filesystem": "^7.1", + "symfony/lock": "^7.1", + "symfony/process": "^7.1", + "symfony/string": "^7.1" }, "require-dev": { "devizzent/cebe-php-openapi": "^1.0.1", "devster/ubench": "^2.1", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-doctrine": "^1.4", + "phpstan/phpstan": "^1.12", + "phpstan/phpstan-doctrine": "^1.5", "phpstan/phpstan-phpunit": "^1.4", "phpstan/phpstan-symfony": "^1.4", "phpunit/php-code-coverage": "^11.0", "phpunit/phpcov": "^10.0", - "phpunit/phpunit": "^11.3", + "phpunit/phpunit": "^11.4", "roave/security-advisories": "dev-master", "shlinkio/php-coding-standard": "~2.3.0", - "shlinkio/shlink-test-utils": "^4.1", - "symfony/var-dumper": "^7.0", - "veewee/composer-run-parallel": "^1.3" + "shlinkio/shlink-test-utils": "^4.1.1", + "symfony/var-dumper": "^7.1", + "veewee/composer-run-parallel": "^1.4" }, "conflict": { "symfony/var-exporter": ">=6.3.9,<=6.4.0" @@ -114,31 +114,47 @@ ], "cs": "phpcs -s", "cs:fix": "phpcbf", - "stan": "APP_ENV=test php vendor/bin/phpstan analyse", + "stan": ["@putenv APP_ENV=test", "phpstan analyse"], "test": [ "@parallel test:unit test:db", "@parallel test:api test:cli" ], - "test:unit": "COLUMNS=120 vendor/bin/phpunit --order-by=random --testdox --testdox-summary", - "test:unit:ci": "@test:unit --coverage-php=build/coverage-unit.cov", - "test:unit:pretty": "@test:unit --coverage-html build/coverage-unit/coverage-html", + "test:unit": ["@putenv COLUMNS=120", "phpunit --order-by=random --testdox --testdox-summary"], + "test:unit:ci": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-php=build/coverage-unit.cov"], + "test:unit:pretty": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-html build/coverage-unit/coverage-html"], "test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms", - "test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml", - "test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov", - "test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite -- $*", - "test:db:maria": "DB_DRIVER=maria composer test:db:sqlite -- $*", - "test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite -- $*", - "test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite -- $*", + "test:db:sqlite": ["@putenv APP_ENV=test", "phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml"], + "test:db:sqlite:ci": ["@putenv XDEBUG_MODE=coverage", "@test:db:sqlite --coverage-php build/coverage-db.cov"], + "test:db:mysql": ["@putenv DB_DRIVER=mysql", "@test:db:sqlite"], + "test:db:maria": ["@putenv DB_DRIVER=maria", "@test:db:sqlite"], + "test:db:postgres": ["@putenv DB_DRIVER=postgres", "@test:db:sqlite"], + "test:db:ms": ["@putenv DB_DRIVER=mssql", "@test:db:sqlite"], "test:api": "bin/test/run-api-tests.sh", - "test:api:sqlite": "DB_DRIVER=sqlite composer test:api -- $*", - "test:api:mysql": "DB_DRIVER=mysql composer test:api -- $*", - "test:api:maria": "DB_DRIVER=maria composer test:api -- $*", - "test:api:mssql": "DB_DRIVER=mssql composer test:api -- $*", - "test:api:ci": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov", - "test:api:pretty": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov", - "test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-cli.xml", - "test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov", - "test:cli:pretty": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov", + "test:api:sqlite": ["@putenv DB_DRIVER=sqlite", "@test:api"], + "test:api:mysql": ["@putenv DB_DRIVER=mysql", "@test:api"], + "test:api:maria": ["@putenv DB_DRIVER=maria", "@test:api"], + "test:api:mssql": ["@putenv DB_DRIVER=mssql", "@test:api"], + "test:api:ci": [ + "@putenv GENERATE_COVERAGE=yes", + "@test:api", + "phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov" + ], + "test:api:pretty": [ + "@putenv GENERATE_COVERAGE=yes", + "@test:api", + "phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov" + ], + "test:cli": "bin/test/run-cli-tests.sh", + "test:cli:ci": [ + "@putenv GENERATE_COVERAGE=yes", + "@test:cli", + "vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov" + ], + "test:cli:pretty": [ + "@putenv GENERATE_COVERAGE=yes", + "@test:cli", + "phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov" + ], "swagger:validate": "php-openapi validate docs/swagger/swagger.json", "swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json", "clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php" diff --git a/config/autoload/.gitignore b/config/autoload/.gitignore deleted file mode 100644 index 1a83fda62..000000000 --- a/config/autoload/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -local.php -*.local.php diff --git a/config/autoload/app_options.global.php b/config/autoload/app_options.global.php deleted file mode 100644 index 0b7ec9376..000000000 --- a/config/autoload/app_options.global.php +++ /dev/null @@ -1,12 +0,0 @@ - [ - 'name' => 'Shlink', - 'version' => '%SHLINK_VERSION%', - ], - -]; diff --git a/config/autoload/app_options.local.php.dist b/config/autoload/app_options.local.php.dist deleted file mode 100644 index 14633a61f..000000000 --- a/config/autoload/app_options.local.php.dist +++ /dev/null @@ -1,11 +0,0 @@ - [ - 'version' => 'latest', - ], - -]; diff --git a/config/autoload/common.global.php b/config/autoload/common.global.php index c7db57f19..0ae55ce3a 100644 --- a/config/autoload/common.global.php +++ b/config/autoload/common.global.php @@ -3,13 +3,18 @@ declare(strict_types=1); use Laminas\ConfigAggregator\ConfigAggregator; +use Shlinkio\Shlink\Core\Config\EnvVars; -return [ +return (function () { + $isDev = EnvVars::isDevEnv(); - 'debug' => false, + return [ - // Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console - // commands don't generate a cache file that's then used by php-fpm web executions - ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli', + 'debug' => $isDev, -]; + // Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console + // commands don't generate a cache file that's then used by php-fpm web executions + ConfigAggregator::ENABLE_CACHE => ! $isDev && PHP_SAPI !== 'cli', + + ]; +})(); diff --git a/config/autoload/common.local.php.dist b/config/autoload/common.local.php.dist deleted file mode 100644 index f29c74b05..000000000 --- a/config/autoload/common.local.php.dist +++ /dev/null @@ -1,12 +0,0 @@ - true, - ConfigAggregator::ENABLE_CACHE => false, - -]; diff --git a/config/autoload/delete_short_urls.global.php b/config/autoload/delete_short_urls.global.php deleted file mode 100644 index 2d203ea1d..000000000 --- a/config/autoload/delete_short_urls.global.php +++ /dev/null @@ -1,20 +0,0 @@ -loadFromEnv(); - - return [ - - 'delete_short_urls' => [ - 'check_visits_threshold' => $threshold !== null, - 'visits_threshold' => (int) ($threshold ?? DEFAULT_DELETE_SHORT_URL_THRESHOLD), - ], - - ]; -})(); diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 469171cae..0a99d3235 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -11,6 +11,7 @@ use Psr\Http\Message\ServerRequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UploadedFileFactoryInterface; +use Shlinkio\Shlink\Core\Config\EnvVars; use Spiral\RoadRunner\Http\PSR7Worker; use Spiral\RoadRunner\WorkerInterface; use Symfony\Component\Filesystem\Filesystem; @@ -36,7 +37,7 @@ 'lazy_services' => [ 'proxies_target_dir' => 'data/proxies', 'proxies_namespace' => 'ShlinkProxy', - 'write_proxy_files' => true, + 'write_proxy_files' => EnvVars::isProdEnv(), ], ], diff --git a/config/autoload/dependencies.local.php.dist b/config/autoload/dependencies.local.php.dist deleted file mode 100644 index d9569029c..000000000 --- a/config/autoload/dependencies.local.php.dist +++ /dev/null @@ -1,24 +0,0 @@ - [ - 'lazy_services' => [ - 'write_proxy_files' => false, - ], - - 'initializers' => [ - function (ContainerInterface $container, $instance): void { - if ($instance instanceof Log\LoggerAwareInterface) { - $instance->setLogger($container->get(Log\LoggerInterface::class)); - } - }, - ], - ], - -]; diff --git a/config/autoload/entity-manager.local.php.dist b/config/autoload/entity-manager.local.php.dist deleted file mode 100644 index abe5dd877..000000000 --- a/config/autoload/entity-manager.local.php.dist +++ /dev/null @@ -1,46 +0,0 @@ - [ - 'connection' => [ - // MySQL - 'user' => 'root', - 'password' => 'root', - 'driver' => 'pdo_mysql', - 'host' => 'shlink_db_mysql', - 'dbname' => 'shlink', -// 'dbname' => 'shlink_foo', - 'charset' => 'utf8mb4', - - // MariaDB -// 'user' => 'root', -// 'password' => 'root', -// 'driver' => 'pdo_mysql', -// 'host' => 'shlink_db_maria', -// 'dbname' => 'shlink_foo', -// 'charset' => 'utf8mb4', - - // Postgres -// 'user' => 'postgres', -// 'password' => 'root', -// 'driver' => 'pdo_pgsql', -// 'host' => 'shlink_db_postgres', -// 'dbname' => 'shlink_foo', -// 'charset' => 'utf8', - - // MSSQL -// 'user' => 'sa', -// 'password' => 'Passw0rd!', -// 'driver' => 'pdo_sqlsrv', -// 'host' => 'shlink_db_ms', -// 'dbname' => 'shlink_foo', -// 'driverOptions' => [ -// 'TrustServerCertificate' => 'true', -// ], - ], - ], - -]; diff --git a/config/autoload/logger.global.php b/config/autoload/logger.global.php index c7d7d7576..56ba42bba 100644 --- a/config/autoload/logger.global.php +++ b/config/autoload/logger.global.php @@ -14,23 +14,33 @@ use Shlinkio\Shlink\Common\Logger\LoggerType; use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware; use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware; +use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\EventDispatcher\Helper\RequestIdProvider; use Shlinkio\Shlink\EventDispatcher\Util\RequestIdProviderInterface; +use function Shlinkio\Shlink\Config\env; use function Shlinkio\Shlink\Config\runningInRoadRunner; return (static function (): array { + $isDev = EnvVars::isDevEnv(); $common = [ - 'level' => Level::Info->value, + 'level' => $isDev ? Level::Debug->value : Level::Info->value, 'processors' => [RequestIdMiddleware::class], 'line_format' => '[%datetime%] [%extra.' . RequestIdMiddleware::ATTRIBUTE . '%] %channel%.%level_name% - %message%', ]; + // In dev env or the docker container, stream Shlink logs to stderr, otherwise send them to a file + $useStreamForShlinkLogger = $isDev || env('SHLINK_RUNTIME') !== null; + return [ 'logger' => [ - 'Shlink' => [ + 'Shlink' => $useStreamForShlinkLogger ? [ + 'type' => LoggerType::STREAM->value, + 'destination' => 'php://stderr', + ...$common, + ] : [ 'type' => LoggerType::FILE->value, ...$common, ], diff --git a/config/autoload/logger.local.php.dist b/config/autoload/logger.local.php.dist deleted file mode 100644 index fe2c8c54b..000000000 --- a/config/autoload/logger.local.php.dist +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Shlink' => [ - 'type' => LoggerType::STREAM->value, - 'destination' => 'php://stderr', - 'level' => Level::Debug->value, - ], - ], - -]; diff --git a/config/autoload/matomo.global.php b/config/autoload/matomo.global.php deleted file mode 100644 index d7369ea73..000000000 --- a/config/autoload/matomo.global.php +++ /dev/null @@ -1,16 +0,0 @@ - [ - 'enabled' => (bool) EnvVars::MATOMO_ENABLED->loadFromEnv(), - 'base_url' => EnvVars::MATOMO_BASE_URL->loadFromEnv(), - 'site_id' => EnvVars::MATOMO_SITE_ID->loadFromEnv(), - 'api_token' => EnvVars::MATOMO_API_TOKEN->loadFromEnv(), - ], - -]; diff --git a/config/autoload/matomo.local.php.dist b/config/autoload/matomo.local.php.dist deleted file mode 100644 index 2a9404071..000000000 --- a/config/autoload/matomo.local.php.dist +++ /dev/null @@ -1,26 +0,0 @@ - [ -// 'enabled' => true, -// 'base_url' => 'http://shlink_matomo', -// 'site_id' => '...', -// 'api_token' => '...', - ], - -]; diff --git a/config/autoload/mercure.global.php b/config/autoload/mercure.global.php index c50e1f8e7..83abed1a0 100644 --- a/config/autoload/mercure.global.php +++ b/config/autoload/mercure.global.php @@ -8,34 +8,31 @@ use Symfony\Component\Mercure\Hub; use Symfony\Component\Mercure\HubInterface; -return (static function (): array { - $publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv(); +return [ - return [ + // This config is used by shlink-common. Do not delete + 'mercure' => [ + 'public_hub_url' => EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv(), + 'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv(), + 'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(), + 'jwt_issuer' => 'Shlink', + ], - 'mercure' => [ - 'public_hub_url' => $publicUrl, - 'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv(), - 'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(), - 'jwt_issuer' => 'Shlink', - ], - - 'dependencies' => [ - 'delegators' => [ - LcobucciJwtProvider::class => [ - LazyServiceFactory::class, - ], - Hub::class => [ - LazyServiceFactory::class, - ], + 'dependencies' => [ + 'delegators' => [ + LcobucciJwtProvider::class => [ + LazyServiceFactory::class, ], - 'lazy_services' => [ - 'class_map' => [ - LcobucciJwtProvider::class => LcobucciJwtProvider::class, - Hub::class => HubInterface::class, - ], + Hub::class => [ + LazyServiceFactory::class, + ], + ], + 'lazy_services' => [ + 'class_map' => [ + LcobucciJwtProvider::class => LcobucciJwtProvider::class, + Hub::class => HubInterface::class, ], ], + ], - ]; -})(); +]; diff --git a/config/autoload/mercure.local.php.dist b/config/autoload/mercure.local.php.dist deleted file mode 100644 index 13a74022b..000000000 --- a/config/autoload/mercure.local.php.dist +++ /dev/null @@ -1,13 +0,0 @@ - [ - 'public_hub_url' => 'http://localhost:8002', - 'internal_hub_url' => 'http://shlink_mercure_proxy', - 'jwt_secret' => 'mercure_jwt_key_long_enough_to_avoid_error', - ], - -]; diff --git a/config/autoload/qr-codes.global.php b/config/autoload/qr-codes.global.php deleted file mode 100644 index 23caadf2c..000000000 --- a/config/autoload/qr-codes.global.php +++ /dev/null @@ -1,21 +0,0 @@ - [ - 'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(), - 'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(), - 'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(), - 'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(), - 'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(), - 'enabled_for_disabled_short_urls' => (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(), - 'color' => EnvVars::DEFAULT_QR_CODE_COLOR->loadFromEnv(), - 'bg_color' => EnvVars::DEFAULT_QR_CODE_BG_COLOR->loadFromEnv(), - 'logo_url' => EnvVars::DEFAULT_QR_CODE_LOGO_URL->loadFromEnv(), - ], - -]; diff --git a/config/autoload/rabbit.global.php b/config/autoload/rabbit.global.php index 86e2dd6a8..c95323777 100644 --- a/config/autoload/rabbit.global.php +++ b/config/autoload/rabbit.global.php @@ -6,6 +6,7 @@ return [ + // This config is used by shlink-common. Do not delete 'rabbitmq' => [ 'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(), 'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(), diff --git a/config/autoload/rabbit.local.php.dist b/config/autoload/rabbit.local.php.dist deleted file mode 100644 index 37faf181c..000000000 --- a/config/autoload/rabbit.local.php.dist +++ /dev/null @@ -1,15 +0,0 @@ - [ - 'enabled' => true, - 'host' => 'shlink_rabbitmq', - 'port' => 5672, - 'user' => 'rabbit', - 'password' => 'rabbit', - ], - -]; diff --git a/config/autoload/redirects.global.php b/config/autoload/redirects.global.php deleted file mode 100644 index dbf026db8..000000000 --- a/config/autoload/redirects.global.php +++ /dev/null @@ -1,20 +0,0 @@ - [ - 'invalid_short_url' => EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT->loadFromEnv(), - 'regular_404' => EnvVars::DEFAULT_REGULAR_404_REDIRECT->loadFromEnv(), - 'base_url' => EnvVars::DEFAULT_BASE_URL_REDIRECT->loadFromEnv(), - ], - - 'redirects' => [ - 'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(), - 'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(), - ], - -]; diff --git a/config/autoload/redis.local.php.local b/config/autoload/redis.local.php.local deleted file mode 100644 index 7fd57112a..000000000 --- a/config/autoload/redis.local.php.local +++ /dev/null @@ -1,26 +0,0 @@ - [ - 'redis' => [ - 'servers' => 'tcp://shlink_redis:6379', -// 'servers' => 'tcp://barbar@shlink_redis_acl:6379', -// 'servers' => 'tcp://foo:bar@shlink_redis_acl:6379', - ], - ], - - 'redis' => [ - 'pub_sub_enabled' => true, - ], - - 'dependencies' => [ - 'aliases' => [ - // With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default -// 'lock_store' => 'redis_lock_store', - ], - ], - -]; diff --git a/config/autoload/robots.global.php b/config/autoload/robots.global.php deleted file mode 100644 index 35235d72b..000000000 --- a/config/autoload/robots.global.php +++ /dev/null @@ -1,14 +0,0 @@ - [ - 'allow-all-short-urls' => (bool) Config\EnvVars::ROBOTS_ALLOW_ALL_SHORT_URLS->loadFromEnv(), - 'user-agents' => splitByComma(Config\EnvVars::ROBOTS_USER_AGENTS->loadFromEnv()), - ], - -]; diff --git a/config/autoload/router.global.php b/config/autoload/router.global.php index db389f3a2..0464ca834 100644 --- a/config/autoload/router.global.php +++ b/config/autoload/router.global.php @@ -13,7 +13,7 @@ 'fastroute' => [ // Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console // commands don't generate a cache file that's then used by php-fpm web executions - FastRouteRouter::CONFIG_CACHE_ENABLED => PHP_SAPI !== 'cli', + FastRouteRouter::CONFIG_CACHE_ENABLED => EnvVars::isProdEnv() && PHP_SAPI !== 'cli', FastRouteRouter::CONFIG_CACHE_FILE => 'data/cache/fastroute_cached_routes.php', ], ], diff --git a/config/autoload/router.local.php.dist b/config/autoload/router.local.php.dist deleted file mode 100644 index 39bd71694..000000000 --- a/config/autoload/router.local.php.dist +++ /dev/null @@ -1,16 +0,0 @@ - [ -// 'base_path' => '', - 'fastroute' => [ - FastRouteRouter::CONFIG_CACHE_ENABLED => false, - ], - ], - -]; diff --git a/config/autoload/routes.config.php b/config/autoload/routes.config.php index f7c6ce073..da3d17784 100644 --- a/config/autoload/routes.config.php +++ b/config/autoload/routes.config.php @@ -19,8 +19,6 @@ return (static function (): array { $dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class; $overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class; - - // TODO This should be based on config, not the env var $shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv() ? '[/]' : ''; return [ diff --git a/config/autoload/tracking.global.php b/config/autoload/tracking.global.php deleted file mode 100644 index 713a209e4..000000000 --- a/config/autoload/tracking.global.php +++ /dev/null @@ -1,38 +0,0 @@ - [ - // Tells if IP addresses should be anonymized before persisting, to fulfil data protection regulations - // This applies only if IP address tracking is enabled - 'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR->loadFromEnv(), - - // Tells if visits to not-found URLs should be tracked. The disable_tracking option takes precedence - 'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(), - - // A query param that, if provided, will disable tracking of one particular visit. Always takes precedence - 'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(), - - // If true, visits will not be tracked at all - 'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(), - - // If true, visits will be tracked, but neither the IP address, nor the location will be resolved - 'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(), - - // If true, the referrer will not be tracked - 'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(), - - // If true, the user agent will not be tracked - 'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(), - - // A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default - 'disable_tracking_from' => splitByComma(EnvVars::DISABLE_TRACKING_FROM->loadFromEnv()), - ], - -]; diff --git a/config/autoload/url-shortener.global.php b/config/autoload/url-shortener.global.php deleted file mode 100644 index 98b07ea24..000000000 --- a/config/autoload/url-shortener.global.php +++ /dev/null @@ -1,34 +0,0 @@ -loadFromEnv(), - MIN_SHORT_CODES_LENGTH, - ); - $modeFromEnv = EnvVars::SHORT_URL_MODE->loadFromEnv(); - $mode = ShortUrlMode::tryFrom($modeFromEnv) ?? ShortUrlMode::STRICT; - - return [ - - 'url_shortener' => [ - 'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain - 'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv()) ? 'https' : 'http', - 'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(), - ], - 'default_short_codes_length' => $shortCodesLength, - 'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(), - 'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(), - 'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(), - 'trailing_slash_enabled' => (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(), - 'mode' => $mode, - ], - - ]; -})(); diff --git a/config/autoload/url-shortener.local.php.dist b/config/autoload/url-shortener.local.php.dist deleted file mode 100644 index 715d28224..000000000 --- a/config/autoload/url-shortener.local.php.dist +++ /dev/null @@ -1,21 +0,0 @@ - [ - 'domain' => [ - 'schema' => 'http', - 'hostname' => sprintf('localhost:%s', match (true) { - runningInRoadRunner() => '8800', - default => '8000', - }), - ], -// 'multi_segment_slugs_enabled' => true, -// 'trailing_slash_enabled' => true, - ], - -]; diff --git a/config/config.php b/config/config.php index 40f88ea3e..92c69ba03 100644 --- a/config/config.php +++ b/config/config.php @@ -8,10 +8,7 @@ use Laminas\Diactoros; use Mezzio; use Mezzio\ProblemDetails; - -use function Shlinkio\Shlink\Config\env; - -$isTestEnv = env('APP_ENV') === 'test'; +use Shlinkio\Shlink\Core\Config\EnvVars; return (new ConfigAggregator\ConfigAggregator( providers: [ @@ -29,10 +26,10 @@ CLI\ConfigProvider::class, Rest\ConfigProvider::class, new ConfigAggregator\PhpFileProvider('config/autoload/{,*.}global.php'), - // Local config should not be loaded during tests, whereas test config should be loaded ONLY during tests - new ConfigAggregator\PhpFileProvider( - $isTestEnv ? 'config/test/*.global.php' : 'config/autoload/{,*.}local.php', - ), + // Test config should be loaded ONLY during tests + EnvVars::isTestEnv() + ? new ConfigAggregator\PhpFileProvider('config/test/*.global.php') + : new ConfigAggregator\ArrayProvider([]), // Routes have to be loaded last new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'), ], diff --git a/config/container.php b/config/container.php index 3a1e33553..6ac078961 100644 --- a/config/container.php +++ b/config/container.php @@ -15,8 +15,11 @@ require 'vendor/autoload.php'; -// Promote env vars from installer config -loadEnvVarsFromConfig('config/params/generated_config.php', enumValues(EnvVars::class)); +// Promote env vars from installer, dev config or test config +loadEnvVarsFromConfig( + EnvVars::isTestEnv() ? 'config/test/shlink_test_env.php' : 'config/params/*.php', + enumValues(EnvVars::class), +); // This is one of the first files loaded. Configure the timezone and memory limit here ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv()); diff --git a/config/params/.gitignore b/config/params/.gitignore index d6b7ef32c..36565258b 100644 --- a/config/params/.gitignore +++ b/config/params/.gitignore @@ -1,2 +1,3 @@ * !.gitignore +!*.dist diff --git a/config/params/shlink_dev_env.php.dist b/config/params/shlink_dev_env.php.dist new file mode 100644 index 000000000..2a3905dc1 --- /dev/null +++ b/config/params/shlink_dev_env.php.dist @@ -0,0 +1,76 @@ +value => 'dev', +// EnvVars::GEOLITE_LICENSE_KEY->value => '', + + // URL shortener + EnvVars::DEFAULT_DOMAIN->value => 'localhost:8800', + EnvVars::IS_HTTPS_ENABLED->value => false, + + // Database - MySQL + EnvVars::DB_DRIVER->value => 'mysql', + EnvVars::DB_USER->value => 'root', + EnvVars::DB_PASSWORD->value => 'root', + EnvVars::DB_NAME->value => 'shlink', +// EnvVars::DB_NAME->value => 'shlink_foo', + EnvVars::DB_HOST->value => 'shlink_db_mysql', + + // Database - Maria +// EnvVars::DB_DRIVER->value => 'maria', +// EnvVars::DB_USER->value => 'root', +// EnvVars::DB_PASSWORD->value => 'root', +// EnvVars::DB_NAME->value => 'shlink_foo', +// EnvVars::DB_HOST->value => 'shlink_db_maria', + + // Database - Postgres +// EnvVars::DB_DRIVER->value => 'postgres', +// EnvVars::DB_USER->value => 'postgres', +// EnvVars::DB_PASSWORD->value => 'root', +// EnvVars::DB_NAME->value => 'shlink_foo', +// EnvVars::DB_HOST->value => 'shlink_db_postgres', + + // Database - MSSQL +// EnvVars::DB_DRIVER->value => 'mssql', +// EnvVars::DB_USER->value => 'sa', +// EnvVars::DB_PASSWORD->value => 'Passw0rd!', +// EnvVars::DB_NAME->value => 'shlink_foo', +// EnvVars::DB_HOST->value => 'shlink_db_ms', + + // Matomo + // Dev matomo instance needs to be manually configured once before enabling the configuration below: + // 1. Go to http://localhost:8003 and follow the installation instructions. + // 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with + // `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549) + // 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just + // created into the `MATOMO_SITE_ID` var below. + // 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click + // "Create new token" and once generated, paste the token into the `MATOMO_API_TOKEN` var below. + // 5. Copy the config below and paste it in a new shlink-dev.local.env file. + EnvVars::MATOMO_ENABLED->value => false, + EnvVars::MATOMO_BASE_URL->value => 'http://shlink_matomo', +// EnvVars::MATOMO_SITE_ID->value => , +// EnvVars::MATOMO_API_TOKEN->value => , + + // Mercure + EnvVars::MERCURE_PUBLIC_HUB_URL->value => 'http://localhost:8002', + EnvVars::MERCURE_INTERNAL_HUB_URL->value => 'http://shlink_mercure_proxy', + EnvVars::MERCURE_JWT_SECRET->value => 'mercure_jwt_key_long_enough_to_avoid_error', + + // RabbitMQ + EnvVars::RABBITMQ_ENABLED->value => true, + EnvVars::RABBITMQ_HOST->value => 'shlink_rabbitmq', + EnvVars::RABBITMQ_PORT->value => 5672, + EnvVars::RABBITMQ_USER->value => 'rabbit', + EnvVars::RABBITMQ_PASSWORD->value => 'rabbit', + + // Redis + EnvVars::REDIS_PUB_SUB_ENABLED->value => true, + EnvVars::REDIS_SERVERS->value => 'tcp://shlink_redis:6379', + +]; diff --git a/config/test/shlink_test_env.php b/config/test/shlink_test_env.php new file mode 100644 index 000000000..f32d06dba --- /dev/null +++ b/config/test/shlink_test_env.php @@ -0,0 +1,15 @@ +value => 'test', + + // URL shortener + EnvVars::DEFAULT_DOMAIN->value => 's.test', + EnvVars::IS_HTTPS_ENABLED->value => false, + +]; diff --git a/config/test/test_config.global.php b/config/test/test_config.global.php index aad5e9d0a..bd6771e69 100644 --- a/config/test/test_config.global.php +++ b/config/test/test_config.global.php @@ -93,13 +93,6 @@ ConfigAggregator::ENABLE_CACHE => false, FastRouteRouter::CONFIG_CACHE_ENABLED => false, - 'url_shortener' => [ - 'domain' => [ - 'schema' => 'http', - 'hostname' => 's.test', - ], - ], - 'routes' => [ // This route is used to test that title resolution is skipped if the long URL times out [ @@ -120,13 +113,6 @@ ], ], - // Disable mercure integration during E2E tests - 'mercure' => [ - 'public_hub_url' => null, - 'internal_hub_url' => null, - 'jwt_secret' => null, - ], - 'dependencies' => [ 'services' => [ 'shlink_test_api_client' => new Client([ diff --git a/data/infra/php.Dockerfile b/data/infra/php.Dockerfile index 20732e3fe..4a7904bf1 100644 --- a/data/infra/php.Dockerfile +++ b/data/infra/php.Dockerfile @@ -46,12 +46,13 @@ RUN mkdir -p /usr/src/php/ext/apcu \ && rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \ && echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini -# Install pcov and sqlsrv driver -RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ +# Install xdebug and sqlsrv driver +RUN apk add --update linux-headers && \ + wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \ - pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \ - docker-php-ext-enable pdo_sqlsrv pcov && \ + pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \ + docker-php-ext-enable pdo_sqlsrv xdebug && \ apk del .phpize-deps && \ rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk diff --git a/data/infra/php.ini b/data/infra/php.ini index 46ad43bbe..ba8b1620a 100644 --- a/data/infra/php.ini +++ b/data/infra/php.ini @@ -3,5 +3,3 @@ error_reporting=-1 log_errors_max_len=0 zend.assertions=1 assert.exception=1 -pcov.enabled=1 -pcov.directory=module diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile index 33768eda5..0bf251f66 100644 --- a/data/infra/roadrunner.Dockerfile +++ b/data/infra/roadrunner.Dockerfile @@ -46,12 +46,13 @@ RUN mkdir -p /usr/src/php/ext/apcu \ && rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \ && echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini -# Install pcov and sqlsrv driver -RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ +# Install xdebug and sqlsrv driver +RUN apk add --update linux-headers && \ + wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \ - pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \ - docker-php-ext-enable pdo_sqlsrv pcov && \ + pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \ + docker-php-ext-enable pdo_sqlsrv xdebug && \ apk del .phpize-deps && \ rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk @@ -72,5 +73,7 @@ CMD \ if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \ # Download roadrunner binary if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \ - # This forces the app to be started every second until the exit code is 0 - until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done + # Create env file if it does not exist yet + if [[ ! -f "./config/params/shlink_dev_env.php" ]]; then cp ./config/params/shlink_dev_env.php.dist ./config/params/shlink_dev_env.php ; fi && \ + # Run with `exec` so that signals are properly handled + exec ./bin/rr serve -c config/roadrunner/.rr.dev.yml diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 6bc053c33..85a4d6dd2 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,12 +1,15 @@ services: shlink_db_mysql: + user: '0' environment: MYSQL_DATABASE: shlink_test shlink_db_postgres: + user: '0' environment: POSTGRES_DB: shlink_test shlink_db_maria: + user: '0' environment: MYSQL_DATABASE: shlink_test diff --git a/docker-compose.override.yml.dist b/docker-compose.override.yml.dist deleted file mode 100644 index 85e58554a..000000000 --- a/docker-compose.override.yml.dist +++ /dev/null @@ -1,30 +0,0 @@ -services: - shlink_php: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - - shlink_roadrunner: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - - shlink_db_mysql: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - - shlink_db_postgres: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - - shlink_db_maria: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro diff --git a/docker-compose.yml b/docker-compose.yml index 41f54cba3..2b8d1b562 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,7 @@ services: shlink_php: container_name: shlink_php + user: 1000:1000 build: context: . dockerfile: ./data/infra/php.Dockerfile @@ -34,11 +35,13 @@ services: - shlink_matomo environment: LC_ALL: C + DEFAULT_DOMAIN: localhost:8000 extra_hosts: - 'host.docker.internal:host-gateway' shlink_roadrunner: container_name: shlink_roadrunner + user: 1000:1000 build: context: . dockerfile: ./data/infra/roadrunner.Dockerfile @@ -65,6 +68,7 @@ services: shlink_db_mysql: container_name: shlink_db_mysql + user: 1000:1000 image: mysql:8.0 ports: - "3307:3306" @@ -77,6 +81,7 @@ services: shlink_db_postgres: container_name: shlink_db_postgres + user: 1000:1000 image: postgres:16.3-alpine ports: - "5434:5432" @@ -90,6 +95,7 @@ services: shlink_db_maria: container_name: shlink_db_maria + user: 1000:1000 image: mariadb:10.7 ports: - "3308:3306" diff --git a/docker/config/shlink_in_docker.local.php b/docker/config/shlink_in_docker.local.php deleted file mode 100644 index 2d5d6a067..000000000 --- a/docker/config/shlink_in_docker.local.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Shlink' => [ - 'type' => LoggerType::STREAM->value, - 'destination' => 'php://stderr', - ], - ], - -]; diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index e2cce68f2..e1acd118d 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -26,5 +26,6 @@ fi php vendor/bin/shlink-installer init ${flags} if [ "$SHLINK_RUNTIME" = 'rr' ]; then - ./bin/rr serve -c config/roadrunner/.rr.yml + # Run with `exec` so that signals are properly handled + exec ./bin/rr serve -c config/roadrunner/.rr.yml fi diff --git a/docs/adr/2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md b/docs/adr/2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md new file mode 100644 index 000000000..004c2fce5 --- /dev/null +++ b/docs/adr/2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md @@ -0,0 +1,61 @@ +# Handle dev and tests config via env vars instead of local config files + +* Status: Accepted +* Date: 2024-10-24 + +## Context and problem statement + +Due to the tools used by Shlink (Zend Expressive first and Mezzio later), configuration has always been handled via the config aggregator, which is a package that continues with Zend Framework 2 config management philosophy: + +1. Define multiple config files, scoped to their own context, that are merge at runtime. +2. Overwrite with so-called "local" config files, which define values used only during development, and should not be shipped to production. + +However, since Shlink started to support other runtimes and added an official docker image, env vars have started to become a central part of the config definition system. + +That has evolved into a system where production config can be read from env vars, but dev config is expected to be defined via local config files, forcing to maintain two approaches to load config that need to coexist. + +On top of that, keeping dev configs in multiple files makes it harder to keep track of everything. + +Because of that, I'm proposing to switch to an env-var-based approach for dev custom configs, and get rid of local config files. + +## Considered options + +1. Define dev env vars in a single `.env` file which is loaded to containers via docker compose `env-file` option. +2. Define dev env vars in a single `.env` file which is loaded via RoadRunner config. +3. Define dev env vars in a single PHP file returning a map that's then loaded with `loadEnvVarsFromConfig`. +4. Keep local config files and don't change anything. + +## Decision outcome + +Defining env vars in a PHP file has the benefit that any change will take effect immediately, so the decision is to go with option 3. + +## Pros and Cons of the Options + +### 1 - .env file via docker compose + +* Good: because it does not require any special mechanism to feed the env vars into the app. +* Good: because it's a standard format known by many. +* Bad: because dev config gets leaked to tests when run inside the container, breaking some existing ones, and forcing to remember this for future tests. +* Bad: because any change to the env file requires the containers to be manually restarted, or putting some new mechanism in place to restart them automatically. + +### 2 - .env file via RoadRunner + +* Good: because it does not require any special mechanism to feed the env vars into the app. +* Good: because it's a standard format known by many. +* Good: because dev config does not get leaked into tests. +* Bad: because any change to the env file requires the containers to be manually restarted, or putting some new mechanism in place to restart them automatically. + +### 3 - PHP file via `loadEnvVarsFromConfig` + +* Good: because the existing call to `loadEnvVarsFromConfig` can be reused by tweaking a bit the glob pattern, so no new dependencies are needed. +* Good: because dev config does not get leaked into tests, and test-specific env vars can be fed using the same mechanism. +* Good: because changes are picked up instantly by both RoadRunner and php-fpm. +* Good: because env vars can be imported from `EnvVars` class, removing the chances of human mistakes and typos. +* Bad: because people not familiar with the project may not expect env vars to be defined in that format. + +### 4 - keep local config + +* Good: because no changes are needed in the project. +* Bad: because managing multiple local config files makes things harder to maintain. +* Bad: because setting-up the project from scratch requires more steps, or an external package to handle config files. +* Bad: because the project needs to keep two ways to load dev configs, and reading an env var does not warranty you are getting the single source of truth. diff --git a/docs/adr/README.md b/docs/adr/README.md index bafb80b52..fa0e72154 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -2,7 +2,8 @@ Here listed you will find the different architectural decisions taken in the project, including all the reasoning behind it, options considered, and final outcome. -* [2023-07-09Build `latest` docker image only for actual releases](2023-07-09-build-latest-docker-image-only-for-actual-releases.md) +* [2024-10-24 Handle dev and tests config via env vars instead of local config files](2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md) +* [2023-07-09 Build `latest` docker image only for actual releases](2023-07-09-build-latest-docker-image-only-for-actual-releases.md) * [2023-01-06 Support any HTTP method in short URLs](2023-01-06-support-any-http-method-in-short-urls.md) * [2022-08-05 Support multi-segment custom slugs](2022-08-05-support-multi-segment-custom-slugs.md) * [2022-01-15 Update env vars behavior to have precedence over installer options](2022-01-15-update-env-vars-behavior-to-have-precedence-over-installer-options.md) diff --git a/module/CLI/config/dependencies.config.php b/module/CLI/config/dependencies.config.php index 0ee1a3319..2f098998c 100644 --- a/module/CLI/config/dependencies.config.php +++ b/module/CLI/config/dependencies.config.php @@ -7,10 +7,10 @@ use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory; use Laminas\ServiceManager\Factory\InvokableFactory; use Shlinkio\Shlink\Common\Doctrine\NoDbNameConnectionFactory; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\DomainService; use Shlinkio\Shlink\Core\Matomo; -use Shlinkio\Shlink\Core\Options\TrackingOptions; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectRuleService; use Shlinkio\Shlink\Core\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; @@ -88,7 +88,7 @@ TrackingOptions::class, ], Util\ProcessRunner::class => [SymfonyCli\Helper\ProcessHelper::class], - ApiKey\RoleResolver::class => [DomainService::class, 'config.url_shortener.domain.hostname'], + ApiKey\RoleResolver::class => [DomainService::class, UrlShortenerOptions::class], Command\ShortUrl\CreateShortUrlCommand::class => [ ShortUrl\UrlShortener::class, diff --git a/module/CLI/src/ApiKey/RoleResolver.php b/module/CLI/src/ApiKey/RoleResolver.php index 76787451a..ece56c774 100644 --- a/module/CLI/src/ApiKey/RoleResolver.php +++ b/module/CLI/src/ApiKey/RoleResolver.php @@ -5,6 +5,7 @@ namespace Shlinkio\Shlink\CLI\ApiKey; use Shlinkio\Shlink\CLI\Exception\InvalidRoleConfigException; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition; use Shlinkio\Shlink\Rest\ApiKey\Role; @@ -12,11 +13,11 @@ use function is_string; -class RoleResolver implements RoleResolverInterface +readonly class RoleResolver implements RoleResolverInterface { public function __construct( - private readonly DomainServiceInterface $domainService, - private readonly string $defaultDomain, + private DomainServiceInterface $domainService, + private UrlShortenerOptions $urlShortenerOptions, ) { } @@ -39,7 +40,7 @@ public function determineRoles(InputInterface $input): iterable private function resolveRoleForAuthority(string $domainAuthority): RoleDefinition { - if ($domainAuthority === $this->defaultDomain) { + if ($domainAuthority === $this->urlShortenerOptions->defaultDomain) { throw InvalidRoleConfigException::forDomainOnlyWithDefaultDomain(); } diff --git a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php index 0273da71f..fdff2ddc8 100644 --- a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php @@ -6,8 +6,8 @@ use Shlinkio\Shlink\CLI\Input\ShortUrlDataInput; use Shlinkio\Shlink\CLI\Util\ExitCode; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\ShortUrl\UrlShortenerInterface; use Symfony\Component\Console\Command\Command; diff --git a/module/CLI/src/Factory/ApplicationFactory.php b/module/CLI/src/Factory/ApplicationFactory.php index ab716f7ea..89cf42718 100644 --- a/module/CLI/src/Factory/ApplicationFactory.php +++ b/module/CLI/src/Factory/ApplicationFactory.php @@ -5,7 +5,7 @@ namespace Shlinkio\Shlink\CLI\Factory; use Psr\Container\ContainerInterface; -use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\Config\Options\AppOptions; use Symfony\Component\Console\Application as CliApp; use Symfony\Component\Console\CommandLoader\ContainerCommandLoader; diff --git a/module/CLI/src/GeoLite/GeolocationDbUpdater.php b/module/CLI/src/GeoLite/GeolocationDbUpdater.php index ff42c930c..85ae1d3a5 100644 --- a/module/CLI/src/GeoLite/GeolocationDbUpdater.php +++ b/module/CLI/src/GeoLite/GeolocationDbUpdater.php @@ -9,7 +9,7 @@ use GeoIp2\Database\Reader; use MaxMind\Db\Reader\Metadata; use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\IpGeolocation\Exception\DbUpdateException; use Shlinkio\Shlink\IpGeolocation\Exception\MissingLicenseException; use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException; diff --git a/module/CLI/src/Input/ShortUrlDataInput.php b/module/CLI/src/Input/ShortUrlDataInput.php index 10c2da7c0..2d3bf91ea 100644 --- a/module/CLI/src/Input/ShortUrlDataInput.php +++ b/module/CLI/src/Input/ShortUrlDataInput.php @@ -4,7 +4,7 @@ namespace Shlinkio\Shlink\CLI\Input; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlEdition; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; diff --git a/module/CLI/test/ApiKey/RoleResolverTest.php b/module/CLI/test/ApiKey/RoleResolverTest.php index cbd4f0fa1..184780d7d 100644 --- a/module/CLI/test/ApiKey/RoleResolverTest.php +++ b/module/CLI/test/ApiKey/RoleResolverTest.php @@ -10,6 +10,7 @@ use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\ApiKey\RoleResolver; use Shlinkio\Shlink\CLI\Exception\InvalidRoleConfigException; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition; @@ -24,7 +25,7 @@ class RoleResolverTest extends TestCase protected function setUp(): void { $this->domainService = $this->createMock(DomainServiceInterface::class); - $this->resolver = new RoleResolver($this->domainService, 'default.com'); + $this->resolver = new RoleResolver($this->domainService, new UrlShortenerOptions('default.com')); } #[Test, DataProvider('provideRoles')] diff --git a/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php b/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php index 0bc77acab..32240fc51 100644 --- a/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php +++ b/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php @@ -10,10 +10,10 @@ use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Command\Domain\DomainRedirectsCommand; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use ShlinkioTest\Shlink\CLI\Util\CliTestUtils; use Symfony\Component\Console\Tester\CommandTester; diff --git a/module/CLI/test/Command/Domain/ListDomainsCommandTest.php b/module/CLI/test/Command/Domain/ListDomainsCommandTest.php index cfa09e183..2621f9401 100644 --- a/module/CLI/test/Command/Domain/ListDomainsCommandTest.php +++ b/module/CLI/test/Command/Domain/ListDomainsCommandTest.php @@ -11,10 +11,10 @@ use Shlinkio\Shlink\CLI\Command\Domain\ListDomainsCommand; use Shlinkio\Shlink\CLI\Util\ExitCode; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use ShlinkioTest\Shlink\CLI\Util\CliTestUtils; use Symfony\Component\Console\Tester\CommandTester; diff --git a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php index 33031c6bf..19c574819 100644 --- a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php @@ -12,8 +12,8 @@ use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Command\ShortUrl\CreateShortUrlCommand; use Shlinkio\Shlink\CLI\Util\ExitCode; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; @@ -37,10 +37,7 @@ protected function setUp(): void $command = new CreateShortUrlCommand( $this->urlShortener, $this->stringifier, - new UrlShortenerOptions( - domain: ['hostname' => 'example.com', 'schema' => ''], - defaultShortCodesLength: 5, - ), + new UrlShortenerOptions(defaultDomain: 'example.com', defaultShortCodesLength: 5), ); $this->commandTester = CliTestUtils::testerForCommand($command); } diff --git a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php index 6016da1c1..176800abe 100644 --- a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php @@ -37,7 +37,7 @@ protected function setUp(): void { $this->shortUrlService = $this->createMock(ShortUrlListServiceInterface::class); $command = new ListShortUrlsCommand($this->shortUrlService, new ShortUrlDataTransformer( - new ShortUrlStringifier([]), + new ShortUrlStringifier(), )); $this->commandTester = CliTestUtils::testerForCommand($command); } diff --git a/module/CLI/test/Factory/ApplicationFactoryTest.php b/module/CLI/test/Factory/ApplicationFactoryTest.php index 83b0fc66a..88b1280b7 100644 --- a/module/CLI/test/Factory/ApplicationFactoryTest.php +++ b/module/CLI/test/Factory/ApplicationFactoryTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Factory\ApplicationFactory; -use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\Config\Options\AppOptions; use ShlinkioTest\Shlink\CLI\Util\CliTestUtils; class ApplicationFactoryTest extends TestCase diff --git a/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php b/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php index cbb4af5c0..3b0f452e1 100644 --- a/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php +++ b/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php @@ -14,7 +14,7 @@ use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException; use Shlinkio\Shlink\CLI\GeoLite\GeolocationDbUpdater; use Shlinkio\Shlink\CLI\GeoLite\GeolocationResult; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\IpGeolocation\Exception\DbUpdateException; use Shlinkio\Shlink\IpGeolocation\Exception\MissingLicenseException; use Shlinkio\Shlink\IpGeolocation\GeoLite2\DbUpdaterInterface; diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 8f8b609ba..552d5e2a3 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -8,8 +8,7 @@ use Laminas\ServiceManager\Factory\InvokableFactory; use Psr\EventDispatcher\EventDispatcherInterface; use Shlinkio\Shlink\Common\Doctrine\EntityRepositoryFactory; -use Shlinkio\Shlink\Config\Factory\ValinorConfigFactory; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Importer\ImportedLinksProcessorInterface; use Shlinkio\Shlink\IpGeolocation\Resolver\IpLocationResolverInterface; @@ -24,15 +23,15 @@ ErrorHandler\NotFoundRedirectHandler::class => ConfigAbstractFactory::class, ErrorHandler\NotFoundTemplateHandler::class => InvokableFactory::class, - Options\AppOptions::class => [ValinorConfigFactory::class, 'config.app_options'], - Options\DeleteShortUrlsOptions::class => [ValinorConfigFactory::class, 'config.delete_short_urls'], - Options\NotFoundRedirectOptions::class => [ValinorConfigFactory::class, 'config.not_found_redirects'], - Options\RedirectOptions::class => [ValinorConfigFactory::class, 'config.redirects'], - Options\UrlShortenerOptions::class => [ValinorConfigFactory::class, 'config.url_shortener'], - Options\TrackingOptions::class => [ValinorConfigFactory::class, 'config.tracking'], - Options\QrCodeOptions::class => [ValinorConfigFactory::class, 'config.qr_codes'], - Options\RabbitMqOptions::class => [ValinorConfigFactory::class, 'config.rabbitmq'], - Options\RobotsOptions::class => [ValinorConfigFactory::class, 'config.robots'], + Config\Options\AppOptions::class => [Config\Options\AppOptions::class, 'fromEnv'], + Config\Options\DeleteShortUrlsOptions::class => [Config\Options\DeleteShortUrlsOptions::class, 'fromEnv'], + Config\Options\NotFoundRedirectOptions::class => [Config\Options\NotFoundRedirectOptions::class, 'fromEnv'], + Config\Options\RedirectOptions::class => [Config\Options\RedirectOptions::class, 'fromEnv'], + Config\Options\UrlShortenerOptions::class => [Config\Options\UrlShortenerOptions::class, 'fromEnv'], + Config\Options\TrackingOptions::class => [Config\Options\TrackingOptions::class, 'fromEnv'], + Config\Options\QrCodeOptions::class => [Config\Options\QrCodeOptions::class, 'fromEnv'], + Config\Options\RabbitMqOptions::class => [Config\Options\RabbitMqOptions::class, 'fromEnv'], + Config\Options\RobotsOptions::class => [Config\Options\RobotsOptions::class, 'fromEnv'], RedirectRule\ShortUrlRedirectRuleService::class => ConfigAbstractFactory::class, RedirectRule\ShortUrlRedirectionResolver::class => ConfigAbstractFactory::class, @@ -101,7 +100,7 @@ Crawling\CrawlingHelper::class => ConfigAbstractFactory::class, - Matomo\MatomoOptions::class => [ValinorConfigFactory::class, 'config.matomo'], + Matomo\MatomoOptions::class => [Matomo\MatomoOptions::class, 'fromEnv'], Matomo\MatomoTrackerBuilder::class => ConfigAbstractFactory::class, Matomo\MatomoVisitSender::class => ConfigAbstractFactory::class, ], @@ -137,9 +136,9 @@ Visit\VisitsTracker::class => [ 'em', EventDispatcherInterface::class, - Options\TrackingOptions::class, + Config\Options\TrackingOptions::class, ], - Visit\RequestTracker::class => [Visit\VisitsTracker::class, Options\TrackingOptions::class], + Visit\RequestTracker::class => [Visit\VisitsTracker::class, Config\Options\TrackingOptions::class], Visit\VisitsDeleter::class => [Visit\Repository\VisitDeleterRepository::class], ShortUrl\ShortUrlService::class => [ 'em', @@ -149,7 +148,7 @@ ], ShortUrl\ShortUrlListService::class => [ ShortUrl\Repository\ShortUrlListRepository::class, - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, ], Visit\Geolocation\VisitLocator::class => ['em', Visit\Repository\VisitIterationRepository::class], Visit\Geolocation\VisitToLocationHelper::class => [IpLocationResolverInterface::class], @@ -157,20 +156,20 @@ Tag\TagService::class => ['em'], ShortUrl\DeleteShortUrlService::class => [ 'em', - Options\DeleteShortUrlsOptions::class, + Config\Options\DeleteShortUrlsOptions::class, ShortUrl\ShortUrlResolver::class, ShortUrl\Repository\ExpiredShortUrlsRepository::class, ], - ShortUrl\ShortUrlResolver::class => ['em', Options\UrlShortenerOptions::class], + ShortUrl\ShortUrlResolver::class => ['em', Config\Options\UrlShortenerOptions::class], ShortUrl\ShortUrlVisitsDeleter::class => [ Visit\Repository\VisitDeleterRepository::class, ShortUrl\ShortUrlResolver::class, ], - ShortUrl\Helper\ShortCodeUniquenessHelper::class => ['em', Options\UrlShortenerOptions::class], - Domain\DomainService::class => ['em', 'config.url_shortener.domain.hostname'], + ShortUrl\Helper\ShortCodeUniquenessHelper::class => ['em', Config\Options\UrlShortenerOptions::class], + Domain\DomainService::class => ['em', Config\Options\UrlShortenerOptions::class], Util\DoctrineBatchHelper::class => ['em'], - Util\RedirectResponseHelper::class => [Options\RedirectOptions::class], + Util\RedirectResponseHelper::class => [Config\Options\RedirectOptions::class], Config\NotFoundRedirectResolver::class => [Util\RedirectResponseHelper::class, 'Logger_Shlink'], @@ -188,19 +187,25 @@ ShortUrl\ShortUrlResolver::class, ShortUrl\Helper\ShortUrlStringifier::class, 'Logger_Shlink', - Options\QrCodeOptions::class, + Config\Options\QrCodeOptions::class, ], - Action\RobotsAction::class => [Crawling\CrawlingHelper::class, Options\RobotsOptions::class], + Action\RobotsAction::class => [Crawling\CrawlingHelper::class, Config\Options\RobotsOptions::class], ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class => [ 'em', - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, Lock\LockFactory::class, ], - ShortUrl\Helper\ShortUrlStringifier::class => ['config.url_shortener.domain', 'config.router.base_path'], - ShortUrl\Helper\ShortUrlTitleResolutionHelper::class => ['httpClient', Options\UrlShortenerOptions::class], + ShortUrl\Helper\ShortUrlStringifier::class => [ + Config\Options\UrlShortenerOptions::class, + 'config.router.base_path', + ], + ShortUrl\Helper\ShortUrlTitleResolutionHelper::class => [ + 'httpClient', + Config\Options\UrlShortenerOptions::class, + ], ShortUrl\Helper\ShortUrlRedirectionBuilder::class => [ - Options\TrackingOptions::class, + Config\Options\TrackingOptions::class, RedirectRule\ShortUrlRedirectionResolver::class, ], ShortUrl\Transformer\ShortUrlDataTransformer::class => [ShortUrl\Helper\ShortUrlStringifier::class], @@ -209,9 +214,9 @@ Visit\RequestTracker::class, ShortUrl\Helper\ShortUrlRedirectionBuilder::class, Util\RedirectResponseHelper::class, - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, ], - ShortUrl\Middleware\TrimTrailingSlashMiddleware::class => [Options\UrlShortenerOptions::class], + ShortUrl\Middleware\TrimTrailingSlashMiddleware::class => [Config\Options\UrlShortenerOptions::class], EventDispatcher\PublishingUpdatesGenerator::class => [ShortUrl\Transformer\ShortUrlDataTransformer::class], diff --git a/module/Core/config/event_dispatcher.config.php b/module/Core/config/event_dispatcher.config.php index f401f255a..2491d6064 100644 --- a/module/Core/config/event_dispatcher.config.php +++ b/module/Core/config/event_dispatcher.config.php @@ -129,14 +129,14 @@ EventDispatcher\PublishingUpdatesGenerator::class, 'em', 'Logger_Shlink', - Options\RabbitMqOptions::class, + Config\Options\RabbitMqOptions::class, ], EventDispatcher\RabbitMq\NotifyNewShortUrlToRabbitMq::class => [ RabbitMqPublishingHelper::class, EventDispatcher\PublishingUpdatesGenerator::class, 'em', 'Logger_Shlink', - Options\RabbitMqOptions::class, + Config\Options\RabbitMqOptions::class, ], EventDispatcher\RedisPubSub\NotifyVisitToRedis::class => [ RedisPublishingHelper::class, @@ -167,7 +167,7 @@ ], EventDispatcher\Helper\EnabledListenerChecker::class => [ - Options\RabbitMqOptions::class, + Config\Options\RabbitMqOptions::class, 'config.redis.pub_sub_enabled', MercureOptions::class, GeoLite2Options::class, diff --git a/module/Core/src/Action/Model/QrCodeParams.php b/module/Core/src/Action/Model/QrCodeParams.php index 2a3907cc6..46d90056e 100644 --- a/module/Core/src/Action/Model/QrCodeParams.php +++ b/module/Core/src/Action/Model/QrCodeParams.php @@ -12,7 +12,7 @@ use Endroid\QrCode\Writer\SvgWriter; use Endroid\QrCode\Writer\WriterInterface; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\QrCodeOptions; +use Shlinkio\Shlink\Core\Config\Options\QrCodeOptions; use function ctype_xdigit; use function hexdec; diff --git a/module/Core/src/Action/QrCodeAction.php b/module/Core/src/Action/QrCodeAction.php index 53fb12514..607fab50b 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -5,6 +5,7 @@ namespace Shlinkio\Shlink\Core\Action; use Endroid\QrCode\Builder\Builder; +use Endroid\QrCode\Writer\Result\ResultInterface; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\MiddlewareInterface; @@ -12,8 +13,8 @@ use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Common\Response\QrCodeResponse; use Shlinkio\Shlink\Core\Action\Model\QrCodeParams; +use Shlinkio\Shlink\Core\Config\Options\QrCodeOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\QrCodeOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface; @@ -42,22 +43,30 @@ public function process(Request $request, RequestHandlerInterface $handler): Res } $params = QrCodeParams::fromRequest($request, $this->options); - $qrCodeBuilder = Builder::create() - ->data($this->stringifier->stringify($shortUrl)) - ->size($params->size) - ->margin($params->margin) - ->writer($params->writer) - ->errorCorrectionLevel($params->errorCorrectionLevel) - ->roundBlockSizeMode($params->roundBlockSizeMode) - ->foregroundColor($params->color) - ->backgroundColor($params->bgColor); + $qrCodeBuilder = new Builder( + writer: $params->writer, + data: $this->stringifier->stringify($shortUrl), + errorCorrectionLevel: $params->errorCorrectionLevel, + size: $params->size, + margin: $params->margin, + roundBlockSizeMode: $params->roundBlockSizeMode, + foregroundColor: $params->color, + backgroundColor: $params->bgColor, + ); + return new QrCodeResponse($this->buildQrCode($qrCodeBuilder, $params)); + } + + private function buildQrCode(Builder $qrCodeBuilder, QrCodeParams $params): ResultInterface + { $logoUrl = $this->options->logoUrl; - if ($logoUrl !== null) { - $qrCodeBuilder->logoPath($logoUrl) - ->logoResizeToHeight((int) ($params->size / 4)); + if ($logoUrl === null) { + return $qrCodeBuilder->build(); } - return new QrCodeResponse($qrCodeBuilder->build()); + return $qrCodeBuilder->build( + logoPath: $logoUrl, + logoResizeToHeight: (int) ($params->size / 4), + ); } } diff --git a/module/Core/src/Action/RobotsAction.php b/module/Core/src/Action/RobotsAction.php index 29c2c8d28..21b7d21e5 100644 --- a/module/Core/src/Action/RobotsAction.php +++ b/module/Core/src/Action/RobotsAction.php @@ -9,8 +9,8 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\RobotsOptions; use Shlinkio\Shlink\Core\Crawling\CrawlingHelperInterface; -use Shlinkio\Shlink\Core\Options\RobotsOptions; use function sprintf; diff --git a/module/Core/src/Config/EnvVars.php b/module/Core/src/Config/EnvVars.php index 2fe5dfd9e..6cdf62974 100644 --- a/module/Core/src/Config/EnvVars.php +++ b/module/Core/src/Config/EnvVars.php @@ -27,6 +27,7 @@ enum EnvVars: string { + case APP_ENV = 'APP_ENV'; case DELETE_SHORT_URL_THRESHOLD = 'DELETE_SHORT_URL_THRESHOLD'; case DB_DRIVER = 'DB_DRIVER'; case DB_NAME = 'DB_NAME'; @@ -117,6 +118,7 @@ private function loadFromFileEnv(): string|int|bool|null private function defaultValue(): string|int|bool|null { return match ($this) { + self::APP_ENV => 'prod', self::MEMORY_LIMIT => '512M', self::TIMEZONE => date_default_timezone_get(), @@ -174,4 +176,19 @@ public function existsInEnv(): bool { return $this->loadFromEnv() !== null; } + + public static function isProdEnv(): bool + { + return self::APP_ENV->loadFromEnv() === 'prod'; + } + + public static function isDevEnv(): bool + { + return self::APP_ENV->loadFromEnv() === 'dev'; + } + + public static function isTestEnv(): bool + { + return self::APP_ENV->loadFromEnv() === 'test'; + } } diff --git a/module/Core/src/Config/Options/AppOptions.php b/module/Core/src/Config/Options/AppOptions.php new file mode 100644 index 000000000..71e3f507c --- /dev/null +++ b/module/Core/src/Config/Options/AppOptions.php @@ -0,0 +1,27 @@ +name, $this->version); + } +} diff --git a/module/Core/src/Config/Options/DeleteShortUrlsOptions.php b/module/Core/src/Config/Options/DeleteShortUrlsOptions.php new file mode 100644 index 000000000..97916d36a --- /dev/null +++ b/module/Core/src/Config/Options/DeleteShortUrlsOptions.php @@ -0,0 +1,28 @@ +loadFromEnv(); + + return new self( + visitsThreshold: (int) ($threshold ?? DEFAULT_DELETE_SHORT_URL_THRESHOLD), + checkVisitsThreshold: $threshold !== null, + ); + } +} diff --git a/module/Core/src/Options/NotFoundRedirectOptions.php b/module/Core/src/Config/Options/NotFoundRedirectOptions.php similarity index 54% rename from module/Core/src/Options/NotFoundRedirectOptions.php rename to module/Core/src/Config/Options/NotFoundRedirectOptions.php index fe99ac7e5..e6ef6a241 100644 --- a/module/Core/src/Options/NotFoundRedirectOptions.php +++ b/module/Core/src/Config/Options/NotFoundRedirectOptions.php @@ -2,19 +2,29 @@ declare(strict_types=1); -namespace Shlinkio\Shlink\Core\Options; +namespace Shlinkio\Shlink\Core\Config\Options; +use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\Config\NotFoundRedirectConfigInterface; -final class NotFoundRedirectOptions implements NotFoundRedirectConfigInterface +final readonly class NotFoundRedirectOptions implements NotFoundRedirectConfigInterface { public function __construct( - public readonly ?string $invalidShortUrl = null, - public readonly ?string $regular404 = null, - public readonly ?string $baseUrl = null, + public ?string $invalidShortUrl = null, + public ?string $regular404 = null, + public ?string $baseUrl = null, ) { } + public static function fromEnv(): self + { + return new self( + invalidShortUrl: EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT->loadFromEnv(), + regular404: EnvVars::DEFAULT_REGULAR_404_REDIRECT->loadFromEnv(), + baseUrl: EnvVars::DEFAULT_BASE_URL_REDIRECT->loadFromEnv(), + ); + } + public function invalidShortUrlRedirect(): ?string { return $this->invalidShortUrl; diff --git a/module/Core/src/Options/QrCodeOptions.php b/module/Core/src/Config/Options/QrCodeOptions.php similarity index 54% rename from module/Core/src/Options/QrCodeOptions.php rename to module/Core/src/Config/Options/QrCodeOptions.php index da130d17b..4d85e6ccd 100644 --- a/module/Core/src/Options/QrCodeOptions.php +++ b/module/Core/src/Config/Options/QrCodeOptions.php @@ -2,7 +2,9 @@ declare(strict_types=1); -namespace Shlinkio\Shlink\Core\Options; +namespace Shlinkio\Shlink\Core\Config\Options; + +use Shlinkio\Shlink\Core\Config\EnvVars; use const Shlinkio\Shlink\DEFAULT_QR_CODE_BG_COLOR; use const Shlinkio\Shlink\DEFAULT_QR_CODE_COLOR; @@ -13,7 +15,7 @@ use const Shlinkio\Shlink\DEFAULT_QR_CODE_ROUND_BLOCK_SIZE; use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE; -readonly final class QrCodeOptions +final readonly class QrCodeOptions { public function __construct( public int $size = DEFAULT_QR_CODE_SIZE, @@ -27,4 +29,19 @@ public function __construct( public ?string $logoUrl = null, ) { } + + public static function fromEnv(): self + { + return new self( + size: (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(), + margin: (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(), + format: EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(), + errorCorrection: EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(), + roundBlockSize: (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(), + enabledForDisabledShortUrls: (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(), + color: EnvVars::DEFAULT_QR_CODE_COLOR->loadFromEnv(), + bgColor: EnvVars::DEFAULT_QR_CODE_BG_COLOR->loadFromEnv(), + logoUrl: EnvVars::DEFAULT_QR_CODE_LOGO_URL->loadFromEnv(), + ); + } } diff --git a/module/Core/src/Config/Options/RabbitMqOptions.php b/module/Core/src/Config/Options/RabbitMqOptions.php new file mode 100644 index 000000000..4bc86e34a --- /dev/null +++ b/module/Core/src/Config/Options/RabbitMqOptions.php @@ -0,0 +1,19 @@ +loadFromEnv()); + } +} diff --git a/module/Core/src/Options/RedirectOptions.php b/module/Core/src/Config/Options/RedirectOptions.php similarity index 59% rename from module/Core/src/Options/RedirectOptions.php rename to module/Core/src/Config/Options/RedirectOptions.php index dd9f0a6d6..75faf0977 100644 --- a/module/Core/src/Options/RedirectOptions.php +++ b/module/Core/src/Config/Options/RedirectOptions.php @@ -2,18 +2,19 @@ declare(strict_types=1); -namespace Shlinkio\Shlink\Core\Options; +namespace Shlinkio\Shlink\Core\Config\Options; use Fig\Http\Message\StatusCodeInterface; +use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\Util\RedirectStatus; use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME; use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE; -final class RedirectOptions +final readonly class RedirectOptions { - public readonly RedirectStatus $redirectStatusCode; - public readonly int $redirectCacheLifetime; + public RedirectStatus $redirectStatusCode; + public int $redirectCacheLifetime; public function __construct( int $redirectStatusCode = StatusCodeInterface::STATUS_FOUND, @@ -24,4 +25,12 @@ public function __construct( ? $redirectCacheLifetime : DEFAULT_REDIRECT_CACHE_LIFETIME; } + + public static function fromEnv(): self + { + return new self( + redirectStatusCode: (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(), + redirectCacheLifetime: (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(), + ); + } } diff --git a/module/Core/src/Config/Options/RobotsOptions.php b/module/Core/src/Config/Options/RobotsOptions.php new file mode 100644 index 000000000..6a8c364ca --- /dev/null +++ b/module/Core/src/Config/Options/RobotsOptions.php @@ -0,0 +1,33 @@ +loadFromEnv(), + userAgents: splitByComma(EnvVars::ROBOTS_USER_AGENTS->loadFromEnv()), + ); + } + + public function hasUserAgents(): bool + { + return count($this->userAgents) > 0; + } +} diff --git a/module/Core/src/Config/Options/TrackingOptions.php b/module/Core/src/Config/Options/TrackingOptions.php new file mode 100644 index 000000000..eddfba345 --- /dev/null +++ b/module/Core/src/Config/Options/TrackingOptions.php @@ -0,0 +1,62 @@ +loadFromEnv(), + trackOrphanVisits: (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(), + disableTrackParam: EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(), + disableTracking: (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(), + disableIpTracking: (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(), + disableReferrerTracking: (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(), + disableUaTracking: (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(), + disableTrackingFrom: splitByComma(EnvVars::DISABLE_TRACKING_FROM->loadFromEnv()), + ); + } + + public function hasDisableTrackingFrom(): bool + { + return ! empty($this->disableTrackingFrom); + } + + public function queryHasDisableTrackParam(array $query): bool + { + return $this->disableTrackParam !== null && array_key_exists($this->disableTrackParam, $query); + } +} diff --git a/module/Core/src/Config/Options/UrlShortenerOptions.php b/module/Core/src/Config/Options/UrlShortenerOptions.php new file mode 100644 index 000000000..98b450b4d --- /dev/null +++ b/module/Core/src/Config/Options/UrlShortenerOptions.php @@ -0,0 +1,56 @@ +loadFromEnv(), + MIN_SHORT_CODES_LENGTH, + ); + $mode = EnvVars::SHORT_URL_MODE->loadFromEnv(); + + return new self( + defaultDomain: EnvVars::DEFAULT_DOMAIN->loadFromEnv(), + schema: ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv()) ? 'https' : 'http', + defaultShortCodesLength: $shortCodesLength, + autoResolveTitles: (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(), + appendExtraPath: (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(), + multiSegmentSlugsEnabled: (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(), + trailingSlashEnabled: (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(), + mode: ShortUrlMode::tryFrom($mode) ?? ShortUrlMode::STRICT, + ); + } + + public function isLooseMode(): bool + { + return $this->mode === ShortUrlMode::LOOSE; + } +} diff --git a/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php b/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php index 585f78b6b..c4078890b 100644 --- a/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php +++ b/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php @@ -4,6 +4,8 @@ namespace Shlinkio\Shlink\Core\Config\PostProcessor; +use Shlinkio\Shlink\Core\Config\EnvVars; + use function array_map; use function str_replace; @@ -14,7 +16,7 @@ class MultiSegmentSlugProcessor public function __invoke(array $config): array { - $multiSegmentEnabled = $config['url_shortener']['multi_segment_slugs_enabled'] ?? false; + $multiSegmentEnabled = (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(); if (! $multiSegmentEnabled) { return $config; } diff --git a/module/Core/src/Domain/DomainService.php b/module/Core/src/Domain/DomainService.php index 93adbf5f1..e514af55a 100644 --- a/module/Core/src/Domain/DomainService.php +++ b/module/Core/src/Domain/DomainService.php @@ -7,6 +7,7 @@ use Doctrine\ORM\EntityManagerInterface; use Shlinkio\Shlink\Core\Config\EmptyNotFoundRedirectConfig; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; use Shlinkio\Shlink\Core\Domain\Repository\DomainRepositoryInterface; @@ -16,9 +17,9 @@ use function array_map; -class DomainService implements DomainServiceInterface +readonly class DomainService implements DomainServiceInterface { - public function __construct(private readonly EntityManagerInterface $em, private readonly string $defaultDomain) + public function __construct(private EntityManagerInterface $em, private UrlShortenerOptions $urlShortenerOptions) { } @@ -35,7 +36,10 @@ public function listDomains(?ApiKey $apiKey = null): array } return [ - DomainItem::forDefaultDomain($this->defaultDomain, $default ?? new EmptyNotFoundRedirectConfig()), + DomainItem::forDefaultDomain( + $this->urlShortenerOptions->defaultDomain, + $default ?? new EmptyNotFoundRedirectConfig(), + ), ...$mappedDomains, ]; } @@ -52,7 +56,7 @@ private function defaultDomainAndRest(?ApiKey $apiKey): array $restOfDomains = []; foreach ($allDomains as $domain) { - if ($domain->authority === $this->defaultDomain) { + if ($domain->authority === $this->urlShortenerOptions->defaultDomain) { $defaultDomain = $domain; } else { $restOfDomains[] = $domain; diff --git a/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php b/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php index 4138a72e6..4e7360d5a 100644 --- a/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php +++ b/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php @@ -9,15 +9,15 @@ use Psr\Http\Message\UriInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config; use Shlinkio\Shlink\Core\Config\NotFoundRedirectResolverInterface; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; -use Shlinkio\Shlink\Core\Options; -class NotFoundRedirectHandler implements MiddlewareInterface +readonly class NotFoundRedirectHandler implements MiddlewareInterface { public function __construct( - private Options\NotFoundRedirectOptions $redirectOptions, + private Config\Options\NotFoundRedirectOptions $redirectOptions, private NotFoundRedirectResolverInterface $redirectResolver, private DomainServiceInterface $domainService, ) { diff --git a/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php b/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php index ad4c80700..c348cbbd8 100644 --- a/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php +++ b/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php @@ -5,9 +5,9 @@ namespace Shlinkio\Shlink\Core\EventDispatcher\Helper; use Shlinkio\Shlink\Common\Mercure\MercureOptions; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher; use Shlinkio\Shlink\Core\Matomo\MatomoOptions; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; use Shlinkio\Shlink\EventDispatcher\Listener\EnabledListenerCheckerInterface; use Shlinkio\Shlink\IpGeolocation\GeoLite2\GeoLite2Options; diff --git a/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php b/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php index daa7cafb3..b8c18fb63 100644 --- a/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php +++ b/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php @@ -7,10 +7,10 @@ use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Async\AbstractNotifyNewShortUrlListener; use Shlinkio\Shlink\Core\EventDispatcher\Async\RemoteSystem; use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; class NotifyNewShortUrlToRabbitMq extends AbstractNotifyNewShortUrlListener { diff --git a/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php b/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php index ddc4221c7..bb55f1696 100644 --- a/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php +++ b/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php @@ -7,10 +7,10 @@ use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Async\AbstractNotifyVisitListener; use Shlinkio\Shlink\Core\EventDispatcher\Async\RemoteSystem; use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; class NotifyVisitToRabbitMq extends AbstractNotifyVisitListener { diff --git a/module/Core/src/Matomo/MatomoOptions.php b/module/Core/src/Matomo/MatomoOptions.php index 235993211..af0ace92d 100644 --- a/module/Core/src/Matomo/MatomoOptions.php +++ b/module/Core/src/Matomo/MatomoOptions.php @@ -4,17 +4,31 @@ namespace Shlinkio\Shlink\Core\Matomo; -class MatomoOptions +use Shlinkio\Shlink\Core\Config\EnvVars; + +final readonly class MatomoOptions { + /** + * @param numeric-string|int|null $siteId + */ public function __construct( - public readonly bool $enabled = false, - public readonly ?string $baseUrl = null, - /** @var numeric-string|int|null */ - private readonly string|int|null $siteId = null, - public readonly ?string $apiToken = null, + public bool $enabled = false, + public ?string $baseUrl = null, + private string|int|null $siteId = null, + public ?string $apiToken = null, ) { } + public static function fromEnv(): self + { + return new self( + enabled: (bool) EnvVars::MATOMO_ENABLED->loadFromEnv(), + baseUrl: EnvVars::MATOMO_BASE_URL->loadFromEnv(), + siteId: EnvVars::MATOMO_SITE_ID->loadFromEnv(), + apiToken: EnvVars::MATOMO_API_TOKEN->loadFromEnv(), + ); + } + public function siteId(): ?int { if ($this->siteId === null) { diff --git a/module/Core/src/Options/AppOptions.php b/module/Core/src/Options/AppOptions.php deleted file mode 100644 index ec545352b..000000000 --- a/module/Core/src/Options/AppOptions.php +++ /dev/null @@ -1,19 +0,0 @@ -name, $this->version); - } -} diff --git a/module/Core/src/Options/DeleteShortUrlsOptions.php b/module/Core/src/Options/DeleteShortUrlsOptions.php deleted file mode 100644 index a645181ba..000000000 --- a/module/Core/src/Options/DeleteShortUrlsOptions.php +++ /dev/null @@ -1,16 +0,0 @@ -userAgents) > 0; - } -} diff --git a/module/Core/src/Options/TrackingOptions.php b/module/Core/src/Options/TrackingOptions.php deleted file mode 100644 index d42723748..000000000 --- a/module/Core/src/Options/TrackingOptions.php +++ /dev/null @@ -1,33 +0,0 @@ -disableTrackingFrom); - } - - public function queryHasDisableTrackParam(array $query): bool - { - return $this->disableTrackParam !== null && array_key_exists($this->disableTrackParam, $query); - } -} diff --git a/module/Core/src/Options/UrlShortenerOptions.php b/module/Core/src/Options/UrlShortenerOptions.php deleted file mode 100644 index 03091d75a..000000000 --- a/module/Core/src/Options/UrlShortenerOptions.php +++ /dev/null @@ -1,36 +0,0 @@ - null, 'hostname' => null], - public readonly int $defaultShortCodesLength = DEFAULT_SHORT_CODES_LENGTH, - public readonly bool $autoResolveTitles = false, - public readonly bool $appendExtraPath = false, - public readonly bool $multiSegmentSlugsEnabled = false, - public readonly bool $trailingSlashEnabled = false, - public readonly ShortUrlMode $mode = ShortUrlMode::STRICT, - ) { - } - - public function isLooseMode(): bool - { - return $this->mode === ShortUrlMode::LOOSE; - } - - public function defaultDomain(): string - { - return $this->domain['hostname'] ?? ''; - } -} diff --git a/module/Core/src/ShortUrl/DeleteShortUrlService.php b/module/Core/src/ShortUrl/DeleteShortUrlService.php index b65381aab..aeb08c47d 100644 --- a/module/Core/src/ShortUrl/DeleteShortUrlService.php +++ b/module/Core/src/ShortUrl/DeleteShortUrlService.php @@ -5,8 +5,8 @@ namespace Shlinkio\Shlink\Core\ShortUrl; use Doctrine\ORM\EntityManagerInterface; +use Shlinkio\Shlink\Core\Config\Options\DeleteShortUrlsOptions; use Shlinkio\Shlink\Core\Exception; -use Shlinkio\Shlink\Core\Options\DeleteShortUrlsOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ExpiredShortUrlsConditions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; diff --git a/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php b/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php index b428019e1..7f863f6cc 100644 --- a/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php +++ b/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php @@ -5,7 +5,7 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Helper; use Doctrine\ORM\EntityManagerInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlRepository; diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php b/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php index 4d801c6fa..375d88370 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php @@ -8,7 +8,7 @@ use GuzzleHttp\Psr7\Uri; use Laminas\Stdlib\ArrayUtils; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectionResolverInterface; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php index 886a4d25d..6659bc0c0 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php @@ -5,22 +5,22 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Helper; use Laminas\Diactoros\Uri; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use function sprintf; -class ShortUrlStringifier implements ShortUrlStringifierInterface +readonly class ShortUrlStringifier implements ShortUrlStringifierInterface { - /** - * @param array{schema?: string, hostname?: string} $domainConfig - */ - public function __construct(private readonly array $domainConfig, private readonly string $basePath = '') - { + public function __construct( + private UrlShortenerOptions $urlShortenerOptions = new UrlShortenerOptions(), + private string $basePath = '', + ) { } public function stringify(ShortUrl $shortUrl): string { - $uriWithoutShortCode = (new Uri())->withScheme($this->domainConfig['schema'] ?? 'http') + $uriWithoutShortCode = (new Uri())->withScheme($this->urlShortenerOptions->schema) ->withHost($this->resolveDomain($shortUrl)) ->withPath($this->basePath) ->__toString(); @@ -31,6 +31,6 @@ public function stringify(ShortUrl $shortUrl): string private function resolveDomain(ShortUrl $shortUrl): string { - return $shortUrl->getDomain()?->authority ?? $this->domainConfig['hostname'] ?? ''; + return $shortUrl->getDomain()?->authority ?? $this->urlShortenerOptions->defaultDomain; } } diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php b/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php index 3f9b62256..0950e042f 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php @@ -8,7 +8,7 @@ use GuzzleHttp\ClientInterface; use GuzzleHttp\RequestOptions; use Psr\Http\Message\ResponseInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Throwable; use function html_entity_decode; diff --git a/module/Core/src/ShortUrl/Middleware/ExtraPathRedirectMiddleware.php b/module/Core/src/ShortUrl/Middleware/ExtraPathRedirectMiddleware.php index 7780f91be..b164ffd68 100644 --- a/module/Core/src/ShortUrl/Middleware/ExtraPathRedirectMiddleware.php +++ b/module/Core/src/ShortUrl/Middleware/ExtraPathRedirectMiddleware.php @@ -9,9 +9,9 @@ use Psr\Http\Message\UriInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlRedirectionBuilderInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface; diff --git a/module/Core/src/ShortUrl/Middleware/TrimTrailingSlashMiddleware.php b/module/Core/src/ShortUrl/Middleware/TrimTrailingSlashMiddleware.php index d3823b27a..0b70c3ae0 100644 --- a/module/Core/src/ShortUrl/Middleware/TrimTrailingSlashMiddleware.php +++ b/module/Core/src/ShortUrl/Middleware/TrimTrailingSlashMiddleware.php @@ -8,7 +8,7 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use function rtrim; diff --git a/module/Core/src/ShortUrl/Model/ShortUrlCreation.php b/module/Core/src/ShortUrl/Model/ShortUrlCreation.php index f53360754..c9c85e1b9 100644 --- a/module/Core/src/ShortUrl/Model/ShortUrlCreation.php +++ b/module/Core/src/ShortUrl/Model/ShortUrlCreation.php @@ -5,8 +5,8 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Model; use Cake\Chronos\Chronos; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\TitleResolutionModelInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; use Shlinkio\Shlink\Rest\Entity\ApiKey; diff --git a/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php b/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php index 2512fc44a..ba4e39599 100644 --- a/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php +++ b/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php @@ -5,7 +5,7 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Model\Validation; use Laminas\Filter\FilterInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use function is_string; use function str_replace; diff --git a/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php b/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php index 24afc72e4..2bc417b42 100644 --- a/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php +++ b/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php @@ -5,7 +5,7 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Model\Validation; use Laminas\Validator\AbstractValidator; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use function is_string; use function strpbrk; diff --git a/module/Core/src/ShortUrl/Model/Validation/ShortUrlInputFilter.php b/module/Core/src/ShortUrl/Model/Validation/ShortUrlInputFilter.php index f4a35969d..3cd7744aa 100644 --- a/module/Core/src/ShortUrl/Model/Validation/ShortUrlInputFilter.php +++ b/module/Core/src/ShortUrl/Model/Validation/ShortUrlInputFilter.php @@ -10,7 +10,7 @@ use Laminas\Validator; use Shlinkio\Shlink\Common\Validation\HostAndPortValidator; use Shlinkio\Shlink\Common\Validation\InputFactory; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Rest\Entity\ApiKey; use function is_string; diff --git a/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php index 3aa6c887e..94fb314a3 100644 --- a/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php +++ b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php @@ -8,8 +8,8 @@ use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Events; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\Entity\Domain; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Tag\Entity\Tag; use Symfony\Component\Lock\Lock; use Symfony\Component\Lock\LockFactory; @@ -40,7 +40,7 @@ public function __construct( public function resolveDomain(?string $domain): ?Domain { - if ($domain === null || $domain === $this->options->defaultDomain()) { + if ($domain === null || $domain === $this->options->defaultDomain) { return null; } diff --git a/module/Core/src/ShortUrl/ShortUrlListService.php b/module/Core/src/ShortUrl/ShortUrlListService.php index f84414547..853a40b93 100644 --- a/module/Core/src/ShortUrl/ShortUrlListService.php +++ b/module/Core/src/ShortUrl/ShortUrlListService.php @@ -5,7 +5,7 @@ namespace Shlinkio\Shlink\Core\ShortUrl; use Shlinkio\Shlink\Common\Paginator\Paginator; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\ShortUrl\Paginator\Adapter\ShortUrlRepositoryAdapter; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlListRepositoryInterface; @@ -24,7 +24,7 @@ public function __construct( */ public function listShortUrls(ShortUrlsParams $params, ?ApiKey $apiKey = null): Paginator { - $defaultDomain = $this->urlShortenerOptions->defaultDomain(); + $defaultDomain = $this->urlShortenerOptions->defaultDomain; $paginator = new Paginator(new ShortUrlRepositoryAdapter($this->repo, $params, $apiKey, $defaultDomain)); $paginator->setMaxPerPage($params->itemsPerPage) ->setCurrentPage($params->page); diff --git a/module/Core/src/ShortUrl/ShortUrlResolver.php b/module/Core/src/ShortUrl/ShortUrlResolver.php index 42d274c00..14727ff56 100644 --- a/module/Core/src/ShortUrl/ShortUrlResolver.php +++ b/module/Core/src/ShortUrl/ShortUrlResolver.php @@ -5,8 +5,8 @@ namespace Shlinkio\Shlink\Core\ShortUrl; use Doctrine\ORM\EntityManagerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlRepository; diff --git a/module/Core/src/Util/RedirectResponseHelper.php b/module/Core/src/Util/RedirectResponseHelper.php index 01e581a7a..edb04b8e2 100644 --- a/module/Core/src/Util/RedirectResponseHelper.php +++ b/module/Core/src/Util/RedirectResponseHelper.php @@ -6,7 +6,7 @@ use Laminas\Diactoros\Response\RedirectResponse; use Psr\Http\Message\ResponseInterface; -use Shlinkio\Shlink\Core\Options\RedirectOptions; +use Shlinkio\Shlink\Core\Config\Options\RedirectOptions; use function sprintf; diff --git a/module/Core/src/Visit/Model/Visitor.php b/module/Core/src/Visit/Model/Visitor.php index 9cf524bcd..ca5d79b2e 100644 --- a/module/Core/src/Visit/Model/Visitor.php +++ b/module/Core/src/Visit/Model/Visitor.php @@ -5,7 +5,7 @@ namespace Shlinkio\Shlink\Core\Visit\Model; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use function Shlinkio\Shlink\Core\ipAddressFromRequest; use function Shlinkio\Shlink\Core\isCrawler; diff --git a/module/Core/src/Visit/RequestTracker.php b/module/Core/src/Visit/RequestTracker.php index 824e7c244..986e221c3 100644 --- a/module/Core/src/Visit/RequestTracker.php +++ b/module/Core/src/Visit/RequestTracker.php @@ -7,9 +7,9 @@ use Fig\Http\Message\RequestMethodInterface; use Mezzio\Router\Middleware\ImplicitHeadMiddleware; use Psr\Http\Message\ServerRequestInterface; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\Exception\InvalidIpFormatException; -use Shlinkio\Shlink\Core\Options\TrackingOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Util\IpAddressUtils; use Shlinkio\Shlink\Core\Visit\Model\Visitor; diff --git a/module/Core/src/Visit/VisitsTracker.php b/module/Core/src/Visit/VisitsTracker.php index f2e26493d..85085220e 100644 --- a/module/Core/src/Visit/VisitsTracker.php +++ b/module/Core/src/Visit/VisitsTracker.php @@ -6,8 +6,8 @@ use Doctrine\ORM; use Psr\EventDispatcher\EventDispatcherInterface; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\EventDispatcher\Event\UrlVisited; -use Shlinkio\Shlink\Core\Options\TrackingOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\Visitor; diff --git a/module/Core/test/Action/QrCodeActionTest.php b/module/Core/test/Action/QrCodeActionTest.php index 08564bf9c..5e499403c 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -16,8 +16,8 @@ use Psr\Log\NullLogger; use Shlinkio\Shlink\Common\Response\QrCodeResponse; use Shlinkio\Shlink\Core\Action\QrCodeAction; +use Shlinkio\Shlink\Core\Config\Options\QrCodeOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\QrCodeOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; @@ -324,7 +324,7 @@ public function action(?QrCodeOptions $options = null): QrCodeAction { return new QrCodeAction( $this->urlResolver, - new ShortUrlStringifier(['domain' => 's.test']), + new ShortUrlStringifier(), new NullLogger(), $options ?? new QrCodeOptions(enabledForDisabledShortUrls: false), ); diff --git a/module/Core/test/Action/RobotsActionTest.php b/module/Core/test/Action/RobotsActionTest.php index 1d83fb8cc..38d80de31 100644 --- a/module/Core/test/Action/RobotsActionTest.php +++ b/module/Core/test/Action/RobotsActionTest.php @@ -10,8 +10,8 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Action\RobotsAction; +use Shlinkio\Shlink\Core\Config\Options\RobotsOptions; use Shlinkio\Shlink\Core\Crawling\CrawlingHelperInterface; -use Shlinkio\Shlink\Core\Options\RobotsOptions; class RobotsActionTest extends TestCase { diff --git a/module/Core/test/Config/NotFoundRedirectResolverTest.php b/module/Core/test/Config/NotFoundRedirectResolverTest.php index 5ef5db2b4..75df09482 100644 --- a/module/Core/test/Config/NotFoundRedirectResolverTest.php +++ b/module/Core/test/Config/NotFoundRedirectResolverTest.php @@ -18,8 +18,8 @@ use Psr\Log\NullLogger; use Shlinkio\Shlink\Core\Action\RedirectAction; use Shlinkio\Shlink\Core\Config\NotFoundRedirectResolver; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface; use function Laminas\Stratigility\middleware; diff --git a/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php b/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php index d811a2e24..487228ec2 100644 --- a/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php +++ b/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php @@ -7,8 +7,11 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\Config\PostProcessor\MultiSegmentSlugProcessor; +use function putenv; + class MultiSegmentSlugProcessorTest extends TestCase { private MultiSegmentSlugProcessor $processor; @@ -18,36 +21,35 @@ protected function setUp(): void $this->processor = new MultiSegmentSlugProcessor(); } + protected function tearDown(): void + { + putenv(EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->value); + } + #[Test, DataProvider('provideConfigs')] - public function parsesRoutesAsExpected(array $config, array $expectedRoutes): void + public function parsesRoutesAsExpected(bool $multiSegmentEnabled, array $routes, array $expectedRoutes): void { - self::assertEquals($expectedRoutes, ($this->processor)($config)['routes'] ?? []); + putenv(EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->value . '=' . ($multiSegmentEnabled ? 'true' : 'false')); + self::assertEquals($expectedRoutes, ($this->processor)(['routes' => $routes])['routes'] ?? []); } public static function provideConfigs(): iterable { - yield [[], []]; - yield [['url_shortener' => []], []]; - yield [['url_shortener' => ['multi_segment_slugs_enabled' => false]], []]; yield [ - [ - 'url_shortener' => ['multi_segment_slugs_enabled' => false], - 'routes' => $routes = [ - ['path' => '/foo'], - ['path' => '/bar/{shortCode}'], - ['path' => '/baz/{shortCode}/foo'], - ], + false, + $routes = [ + ['path' => '/foo'], + ['path' => '/bar/{shortCode}'], + ['path' => '/baz/{shortCode}/foo'], ], $routes, ]; yield [ + true, [ - 'url_shortener' => ['multi_segment_slugs_enabled' => true], - 'routes' => [ - ['path' => '/foo'], - ['path' => '/bar/{shortCode}'], - ['path' => '/baz/{shortCode}/foo'], - ], + ['path' => '/foo'], + ['path' => '/bar/{shortCode}'], + ['path' => '/baz/{shortCode}/foo'], ], [ ['path' => '/foo'], diff --git a/module/Core/test/Domain/DomainServiceTest.php b/module/Core/test/Domain/DomainServiceTest.php index c67597ec8..7e2fea189 100644 --- a/module/Core/test/Domain/DomainServiceTest.php +++ b/module/Core/test/Domain/DomainServiceTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Config\EmptyNotFoundRedirectConfig; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\DomainService; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; @@ -28,7 +29,7 @@ class DomainServiceTest extends TestCase protected function setUp(): void { $this->em = $this->createMock(EntityManagerInterface::class); - $this->domainService = new DomainService($this->em, 'default.com'); + $this->domainService = new DomainService($this->em, new UrlShortenerOptions(defaultDomain: 'default.com')); } #[Test, DataProvider('provideExcludedDomains')] diff --git a/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php b/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php index 53642f3a2..25d9952f2 100644 --- a/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php +++ b/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php @@ -16,11 +16,11 @@ use Psr\Http\Message\UriInterface; use Psr\Http\Server\RequestHandlerInterface; use Shlinkio\Shlink\Core\Config\NotFoundRedirectResolverInterface; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\ErrorHandler\NotFoundRedirectHandler; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; class NotFoundRedirectHandlerTest extends TestCase { diff --git a/module/Core/test/EventDispatcher/Helper/EnabledListenerCheckerTest.php b/module/Core/test/EventDispatcher/Helper/EnabledListenerCheckerTest.php index cebde4374..180ab48d9 100644 --- a/module/Core/test/EventDispatcher/Helper/EnabledListenerCheckerTest.php +++ b/module/Core/test/EventDispatcher/Helper/EnabledListenerCheckerTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Mercure\MercureOptions; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Helper\EnabledListenerChecker; use Shlinkio\Shlink\Core\EventDispatcher\Matomo\SendVisitToMatomo; use Shlinkio\Shlink\Core\EventDispatcher\Mercure\NotifyNewShortUrlToMercure; @@ -18,7 +19,6 @@ use Shlinkio\Shlink\Core\EventDispatcher\RedisPubSub\NotifyVisitToRedis; use Shlinkio\Shlink\Core\EventDispatcher\UpdateGeoLiteDb; use Shlinkio\Shlink\Core\Matomo\MatomoOptions; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; use Shlinkio\Shlink\IpGeolocation\GeoLite2\GeoLite2Options; class EnabledListenerCheckerTest extends TestCase diff --git a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php index bd3c82c83..1ea76bf67 100644 --- a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php +++ b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php @@ -31,7 +31,7 @@ protected function setUp(): void Chronos::setTestNow($this->now); $this->generator = new PublishingUpdatesGenerator( - new ShortUrlDataTransformer(new ShortUrlStringifier([])), + new ShortUrlDataTransformer(new ShortUrlStringifier()), ); } diff --git a/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php b/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php index fc44fd874..b0c5a0e0e 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php @@ -15,11 +15,11 @@ use RuntimeException; use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface; use Shlinkio\Shlink\Common\UpdatePublishing\Update; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Event\ShortUrlCreated; use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; use Shlinkio\Shlink\Core\EventDispatcher\RabbitMq\NotifyNewShortUrlToRabbitMq; use Shlinkio\Shlink\Core\EventDispatcher\Topic; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Throwable; diff --git a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php index 2251d3011..ac744824a 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php @@ -16,10 +16,10 @@ use RuntimeException; use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface; use Shlinkio\Shlink\Common\UpdatePublishing\Update; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated; use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; use Shlinkio\Shlink\Core\EventDispatcher\RabbitMq\NotifyVisitToRabbitMq; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\Visit\Entity\Visit; diff --git a/module/Core/test/Matomo/MatomoVisitSenderTest.php b/module/Core/test/Matomo/MatomoVisitSenderTest.php index 816c8eeaf..6a4659f19 100644 --- a/module/Core/test/Matomo/MatomoVisitSenderTest.php +++ b/module/Core/test/Matomo/MatomoVisitSenderTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Util\DateRange; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Matomo\MatomoTrackerBuilderInterface; use Shlinkio\Shlink\Core\Matomo\MatomoVisitSender; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; @@ -36,7 +37,7 @@ protected function setUp(): void $this->visitSender = new MatomoVisitSender( $this->trackerBuilder, - new ShortUrlStringifier(['hostname' => 's2.test']), + new ShortUrlStringifier(new UrlShortenerOptions(defaultDomain: 's2.test')), $this->visitIterationRepository, ); } diff --git a/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php b/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php index 4788818e8..faddafeb0 100644 --- a/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php +++ b/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php @@ -9,8 +9,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\DeleteShortUrlsOptions; use Shlinkio\Shlink\Core\Exception\DeleteShortUrlException; -use Shlinkio\Shlink\Core\Options\DeleteShortUrlsOptions; use Shlinkio\Shlink\Core\ShortUrl\DeleteShortUrlService; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ExpiredShortUrlsConditions; diff --git a/module/Core/test/ShortUrl/Entity/ShortUrlTest.php b/module/Core/test/ShortUrl/Entity/ShortUrlTest.php index be5b41018..b37202545 100644 --- a/module/Core/test/ShortUrl/Entity/ShortUrlTest.php +++ b/module/Core/test/ShortUrl/Entity/ShortUrlTest.php @@ -9,8 +9,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ShortCodeCannotBeRegeneratedException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode; diff --git a/module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php b/module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php index efbbde21f..de1402f64 100644 --- a/module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php @@ -9,8 +9,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\Entity\Domain; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortCodeUniquenessHelper; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php index afe83c8d8..d1283a78b 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php @@ -12,7 +12,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectionResolverInterface; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlRedirectionBuilder; diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php index e74d61829..d28fdf0e6 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php @@ -7,20 +7,25 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; class ShortUrlStringifierTest extends TestCase { + /** + * @param 'http'|'https' $schema + */ #[Test, DataProvider('provideConfigAndShortUrls')] public function generatesExpectedOutputBasedOnConfigAndShortUrl( - array $config, + string $defaultDomain, + string $schema, string $basePath, ShortUrl $shortUrl, string $expected, ): void { - $stringifier = new ShortUrlStringifier($config, $basePath); + $stringifier = new ShortUrlStringifier(new UrlShortenerOptions($defaultDomain, $schema), $basePath); self::assertEquals($expected, $stringifier->stringify($shortUrl)); } @@ -35,45 +40,45 @@ public static function provideConfigAndShortUrls(): iterable ]), ); - yield 'no config' => [[], '', $shortUrlWithShortCode('foo'), 'http:/foo']; - yield 'hostname in config' => [ - ['hostname' => 'example.com'], + yield 'no default domain' => ['', 'http', '', $shortUrlWithShortCode('foo'), 'http:/foo']; + yield 'default domain' => [ + 'example.com', + 'http', '', $shortUrlWithShortCode('bar'), 'http://example.com/bar', ]; yield 'special chars in short code' => [ - ['hostname' => 'example.com'], + 'example.com', + 'http', '', $shortUrlWithShortCode('グーグル'), 'http://example.com/グーグル', ]; yield 'emojis in short code' => [ - ['hostname' => 'example.com'], + 'example.com', + 'http', '', $shortUrlWithShortCode('🦣-🍅'), 'http://example.com/🦣-🍅', ]; - yield 'hostname with base path in config' => [ - ['hostname' => 'example.com/foo/bar'], + yield 'default domain with base path' => [ + 'example.com/foo/bar', + 'http', '', $shortUrlWithShortCode('abc'), 'http://example.com/foo/bar/abc', ]; - yield 'full config' => [ - ['schema' => 'https', 'hostname' => 'foo.com'], - '', - $shortUrlWithShortCode('baz'), - 'https://foo.com/baz', - ]; yield 'custom domain' => [ - ['schema' => 'https', 'hostname' => 'foo.com'], + 'foo.com', + 'https', '', $shortUrlWithShortCode('baz', 'mydom.es'), 'https://mydom.es/baz', ]; yield 'custom domain with base path' => [ - ['schema' => 'https', 'hostname' => 'foo.com'], + 'foo.com', + 'https', '/foo/bar', $shortUrlWithShortCode('baz', 'mydom.es'), 'https://mydom.es/foo/bar/baz', diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php index 4e3056b3f..b5b8e00c2 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php @@ -16,7 +16,7 @@ use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlTitleResolutionHelper; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; diff --git a/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php b/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php index 3965fe189..6815acb65 100644 --- a/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php +++ b/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php @@ -16,9 +16,9 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Shlinkio\Shlink\Core\Action\RedirectAction; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlRedirectionBuilderInterface; use Shlinkio\Shlink\Core\ShortUrl\Middleware\ExtraPathRedirectMiddleware; diff --git a/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php b/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php index b05ab7d9e..c37c81a5f 100644 --- a/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php +++ b/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Middleware\TrimTrailingSlashMiddleware; class TrimTrailingSlashMiddlewareTest extends TestCase diff --git a/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php b/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php index b84b5b272..e963923b8 100644 --- a/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php +++ b/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php @@ -8,8 +8,8 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; diff --git a/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php b/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php index 290fe63d1..86f695c7d 100644 --- a/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php +++ b/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php @@ -7,7 +7,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\CustomSlugValidator; use stdClass; diff --git a/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php b/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php index 43860909a..722ac347e 100644 --- a/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php +++ b/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php @@ -10,9 +10,9 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Repository\DomainRepository; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver; use Shlinkio\Shlink\Core\Tag\Entity\Tag; use Shlinkio\Shlink\Core\Tag\Repository\TagRepository; @@ -29,9 +29,7 @@ protected function setUp(): void $this->em = $this->createMock(EntityManagerInterface::class); $this->em->method('getEventManager')->willReturn(new EventManager()); - $this->resolver = new PersistenceShortUrlRelationResolver($this->em, new UrlShortenerOptions( - domain: ['schema' => 'https', 'hostname' => 'default.com'], - )); + $this->resolver = new PersistenceShortUrlRelationResolver($this->em, new UrlShortenerOptions('default.com')); } #[Test, DataProvider('provideDomainsThatEmpty')] diff --git a/module/Core/test/ShortUrl/ShortUrlListServiceTest.php b/module/Core/test/ShortUrl/ShortUrlListServiceTest.php index a469ed608..d86637614 100644 --- a/module/Core/test/ShortUrl/ShortUrlListServiceTest.php +++ b/module/Core/test/ShortUrl/ShortUrlListServiceTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlListRepositoryInterface; diff --git a/module/Core/test/ShortUrl/ShortUrlResolverTest.php b/module/Core/test/ShortUrl/ShortUrlResolverTest.php index 1b3aa564a..e8443a13e 100644 --- a/module/Core/test/ShortUrl/ShortUrlResolverTest.php +++ b/module/Core/test/ShortUrl/ShortUrlResolverTest.php @@ -12,8 +12,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; diff --git a/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php b/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php index 349392dbd..9ff014752 100644 --- a/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php +++ b/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php @@ -21,7 +21,7 @@ class ShortUrlDataTransformerTest extends TestCase protected function setUp(): void { - $this->transformer = new ShortUrlDataTransformer(new ShortUrlStringifier([])); + $this->transformer = new ShortUrlDataTransformer(new ShortUrlStringifier()); } #[Test, DataProvider('provideShortUrls')] diff --git a/module/Core/test/Util/RedirectResponseHelperTest.php b/module/Core/test/Util/RedirectResponseHelperTest.php index 63e55e2e2..89d3fa5ae 100644 --- a/module/Core/test/Util/RedirectResponseHelperTest.php +++ b/module/Core/test/Util/RedirectResponseHelperTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\RedirectOptions; +use Shlinkio\Shlink\Core\Config\Options\RedirectOptions; use Shlinkio\Shlink\Core\Util\RedirectResponseHelper; class RedirectResponseHelperTest extends TestCase diff --git a/module/Core/test/Visit/Model/VisitorTest.php b/module/Core/test/Visit/Model/VisitorTest.php index 74065d7dc..04e57179a 100644 --- a/module/Core/test/Visit/Model/VisitorTest.php +++ b/module/Core/test/Visit/Model/VisitorTest.php @@ -7,7 +7,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\Visit\Model\Visitor; use function random_int; diff --git a/module/Core/test/Visit/RequestTrackerTest.php b/module/Core/test/Visit/RequestTrackerTest.php index c8746f91c..ae0a74c4e 100644 --- a/module/Core/test/Visit/RequestTrackerTest.php +++ b/module/Core/test/Visit/RequestTrackerTest.php @@ -13,8 +13,8 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Common\Middleware\IpAddressMiddlewareFactory; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; -use Shlinkio\Shlink\Core\Options\TrackingOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\RequestTracker; diff --git a/module/Core/test/Visit/VisitsTrackerTest.php b/module/Core/test/Visit/VisitsTrackerTest.php index d6cbf4bff..414f5254a 100644 --- a/module/Core/test/Visit/VisitsTrackerTest.php +++ b/module/Core/test/Visit/VisitsTrackerTest.php @@ -10,8 +10,8 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\EventDispatcher\EventDispatcherInterface; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\EventDispatcher\Event\UrlVisited; -use Shlinkio\Shlink\Core\Options\TrackingOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\Visitor; diff --git a/module/Rest/config/dependencies.config.php b/module/Rest/config/dependencies.config.php index d334d5b04..b69cf36d6 100644 --- a/module/Rest/config/dependencies.config.php +++ b/module/Rest/config/dependencies.config.php @@ -10,8 +10,8 @@ use Mezzio\Router\Middleware\ImplicitOptionsMiddleware; use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider; +use Shlinkio\Shlink\Core\Config; use Shlinkio\Shlink\Core\Domain\DomainService; -use Shlinkio\Shlink\Core\Options; use Shlinkio\Shlink\Core\RedirectRule; use Shlinkio\Shlink\Core\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; @@ -64,17 +64,17 @@ ConfigAbstractFactory::class => [ ApiKeyService::class => ['em'], - Action\HealthAction::class => ['em', Options\AppOptions::class], + Action\HealthAction::class => ['em', Config\Options\AppOptions::class], Action\MercureInfoAction::class => [LcobucciJwtProvider::class, 'config.mercure'], Action\ShortUrl\CreateShortUrlAction::class => [ ShortUrl\UrlShortener::class, ShortUrlDataTransformer::class, - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, ], Action\ShortUrl\SingleStepCreateShortUrlAction::class => [ ShortUrl\UrlShortener::class, ShortUrlDataTransformer::class, - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, ], Action\ShortUrl\EditShortUrlAction::class => [ShortUrl\ShortUrlService::class, ShortUrlDataTransformer::class], Action\ShortUrl\DeleteShortUrlAction::class => [ShortUrl\DeleteShortUrlService::class], @@ -86,7 +86,7 @@ Action\Visit\TagVisitsAction::class => [Visit\VisitsStatsHelper::class], Action\Visit\DomainVisitsAction::class => [ Visit\VisitsStatsHelper::class, - 'config.url_shortener.domain.hostname', + Config\Options\UrlShortenerOptions::class, ], Action\Visit\GlobalVisitsAction::class => [Visit\VisitsStatsHelper::class], Action\Visit\OrphanVisitsAction::class => [Visit\VisitsStatsHelper::class], @@ -101,7 +101,7 @@ Action\Tag\TagsStatsAction::class => [TagService::class], Action\Tag\DeleteTagsAction::class => [TagService::class], Action\Tag\UpdateTagAction::class => [TagService::class], - Action\Domain\ListDomainsAction::class => [DomainService::class, Options\NotFoundRedirectOptions::class], + Action\Domain\ListDomainsAction::class => [DomainService::class, Config\Options\NotFoundRedirectOptions::class], Action\Domain\DomainRedirectsAction::class => [DomainService::class], Action\RedirectRule\ListRedirectRulesAction::class => [ ShortUrl\ShortUrlResolver::class, @@ -113,10 +113,10 @@ ], Middleware\CrossDomainMiddleware::class => ['config.cors'], - Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ['config.url_shortener.domain.hostname'], - Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [ - 'config.url_shortener.default_short_codes_length', + Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => [ + Config\Options\UrlShortenerOptions::class, ], + Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [Config\Options\UrlShortenerOptions::class], Middleware\ShortUrl\OverrideDomainMiddleware::class => [DomainService::class], Middleware\Mercure\NotConfiguredMercureErrorHandler::class => [ ProblemDetailsResponseFactory::class, diff --git a/module/Rest/src/Action/Domain/ListDomainsAction.php b/module/Rest/src/Action/Domain/ListDomainsAction.php index e50ada168..d156a7202 100644 --- a/module/Rest/src/Action/Domain/ListDomainsAction.php +++ b/module/Rest/src/Action/Domain/ListDomainsAction.php @@ -8,8 +8,8 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware; diff --git a/module/Rest/src/Action/HealthAction.php b/module/Rest/src/Action/HealthAction.php index 809bf4d18..ce7313309 100644 --- a/module/Rest/src/Action/HealthAction.php +++ b/module/Rest/src/Action/HealthAction.php @@ -8,7 +8,7 @@ use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\Config\Options\AppOptions; use Throwable; class HealthAction extends AbstractRestAction diff --git a/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php b/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php index 65f47d95d..75d5480b5 100644 --- a/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php @@ -7,8 +7,8 @@ use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformerInterface; use Shlinkio\Shlink\Core\ShortUrl\UrlShortenerInterface; diff --git a/module/Rest/src/Action/Visit/DomainVisitsAction.php b/module/Rest/src/Action/Visit/DomainVisitsAction.php index 91f071b5f..4d5342028 100644 --- a/module/Rest/src/Action/Visit/DomainVisitsAction.php +++ b/module/Rest/src/Action/Visit/DomainVisitsAction.php @@ -8,6 +8,7 @@ use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; @@ -20,7 +21,7 @@ class DomainVisitsAction extends AbstractRestAction public function __construct( private readonly VisitsStatsHelperInterface $visitsHelper, - private readonly string $defaultDomain, + private readonly UrlShortenerOptions $urlShortenerOptions, ) { } @@ -37,7 +38,7 @@ public function handle(Request $request): Response private function resolveDomainParam(Request $request): string { $domainParam = $request->getAttribute('domain', ''); - if ($domainParam === $this->defaultDomain) { + if ($domainParam === $this->urlShortenerOptions->defaultDomain) { return 'DEFAULT'; } diff --git a/module/Rest/src/Middleware/ShortUrl/DefaultShortCodesLengthMiddleware.php b/module/Rest/src/Middleware/ShortUrl/DefaultShortCodesLengthMiddleware.php index 4b7779c21..0d7d947e4 100644 --- a/module/Rest/src/Middleware/ShortUrl/DefaultShortCodesLengthMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/DefaultShortCodesLengthMiddleware.php @@ -8,11 +8,12 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; -class DefaultShortCodesLengthMiddleware implements MiddlewareInterface +readonly class DefaultShortCodesLengthMiddleware implements MiddlewareInterface { - public function __construct(private int $defaultShortCodesLength) + public function __construct(private UrlShortenerOptions $urlShortenerOptions) { } @@ -21,7 +22,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface /** @var array $body */ $body = $request->getParsedBody(); if (! isset($body[ShortUrlInputFilter::SHORT_CODE_LENGTH])) { - $body[ShortUrlInputFilter::SHORT_CODE_LENGTH] = $this->defaultShortCodesLength; + $body[ShortUrlInputFilter::SHORT_CODE_LENGTH] = $this->urlShortenerOptions->defaultShortCodesLength; } return $handler->handle($request->withParsedBody($body)); diff --git a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php index 73a6ac696..acf33603c 100644 --- a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php @@ -8,10 +8,11 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; -class DropDefaultDomainFromRequestMiddleware implements MiddlewareInterface +readonly class DropDefaultDomainFromRequestMiddleware implements MiddlewareInterface { - public function __construct(private readonly string $defaultDomain) + public function __construct(private UrlShortenerOptions $urlShortenerOptions) { } @@ -27,7 +28,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface private function sanitizeDomainFromPayload(array $payload): array { - if (isset($payload['domain']) && $payload['domain'] === $this->defaultDomain) { + if (isset($payload['domain']) && $payload['domain'] === $this->urlShortenerOptions->defaultDomain) { unset($payload['domain']); } diff --git a/module/Rest/test/Action/Domain/ListDomainsActionTest.php b/module/Rest/test/Action/Domain/ListDomainsActionTest.php index 41906539e..4864048cf 100644 --- a/module/Rest/test/Action/Domain/ListDomainsActionTest.php +++ b/module/Rest/test/Action/Domain/ListDomainsActionTest.php @@ -10,10 +10,10 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Rest\Action\Domain\ListDomainsAction; use Shlinkio\Shlink\Rest\Entity\ApiKey; diff --git a/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php b/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php index 89b93b804..292c17484 100644 --- a/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php +++ b/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php @@ -8,8 +8,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Config\NotFoundRedirectConfigInterface; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Rest\Action\Domain\Request\DomainRedirectsRequest; class DomainRedirectsRequestTest extends TestCase diff --git a/module/Rest/test/Action/HealthActionTest.php b/module/Rest/test/Action/HealthActionTest.php index 590756461..aae90f49a 100644 --- a/module/Rest/test/Action/HealthActionTest.php +++ b/module/Rest/test/Action/HealthActionTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\Config\Options\AppOptions; use Shlinkio\Shlink\Rest\Action\HealthAction; class HealthActionTest extends TestCase diff --git a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php index 7361df5c2..06cd5554f 100644 --- a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php @@ -12,8 +12,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\UrlShorteningResult; diff --git a/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php index dd7a0e14f..669037d1b 100644 --- a/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php @@ -25,7 +25,7 @@ protected function setUp(): void { $this->shortUrlService = $this->createMock(ShortUrlServiceInterface::class); $this->action = new EditShortUrlAction($this->shortUrlService, new ShortUrlDataTransformer( - new ShortUrlStringifier([]), + new ShortUrlStringifier(), )); } diff --git a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php index d372d6929..a3beba1b0 100644 --- a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Paginator\Paginator; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlListServiceInterface; @@ -30,10 +31,7 @@ protected function setUp(): void $this->service = $this->createMock(ShortUrlListServiceInterface::class); $this->action = new ListShortUrlsAction($this->service, new ShortUrlDataTransformer( - new ShortUrlStringifier([ - 'hostname' => 's.test', - 'schema' => 'https', - ]), + new ShortUrlStringifier(new UrlShortenerOptions('s.test')), )); } diff --git a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php index 0576fd525..13b9e35d2 100644 --- a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php @@ -25,7 +25,7 @@ protected function setUp(): void { $this->urlResolver = $this->createMock(ShortUrlResolverInterface::class); $this->action = new ResolveShortUrlAction($this->urlResolver, new ShortUrlDataTransformer( - new ShortUrlStringifier([]), + new ShortUrlStringifier(), )); } diff --git a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php index 69914f0e7..d28c3b492 100644 --- a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\UrlShorteningResult; diff --git a/module/Rest/test/Action/Visit/DomainVisitsActionTest.php b/module/Rest/test/Action/Visit/DomainVisitsActionTest.php index a6d64bd2b..d60dae2eb 100644 --- a/module/Rest/test/Action/Visit/DomainVisitsActionTest.php +++ b/module/Rest/test/Action/Visit/DomainVisitsActionTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Paginator\Paginator; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Action\Visit\DomainVisitsAction; @@ -24,7 +25,7 @@ class DomainVisitsActionTest extends TestCase protected function setUp(): void { $this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class); - $this->action = new DomainVisitsAction($this->visitsHelper, 'the_default.com'); + $this->action = new DomainVisitsAction($this->visitsHelper, new UrlShortenerOptions('the_default.com')); } #[Test, DataProvider('provideDomainAuthorities')] diff --git a/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php b/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php index e6b0c6ec2..db579451c 100644 --- a/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php +++ b/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; use Shlinkio\Shlink\Rest\Middleware\ShortUrl\DefaultShortCodesLengthMiddleware; @@ -24,7 +25,7 @@ class DefaultShortCodesLengthMiddlewareTest extends TestCase protected function setUp(): void { $this->handler = $this->createMock(RequestHandlerInterface::class); - $this->middleware = new DefaultShortCodesLengthMiddleware(8); + $this->middleware = new DefaultShortCodesLengthMiddleware(new UrlShortenerOptions(defaultShortCodesLength: 8)); } #[Test, DataProvider('provideBodies')] diff --git a/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php b/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php index e9fceea50..b82354982 100644 --- a/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php +++ b/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Rest\Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware; class DropDefaultDomainFromRequestMiddlewareTest extends TestCase @@ -23,7 +24,7 @@ class DropDefaultDomainFromRequestMiddlewareTest extends TestCase protected function setUp(): void { $this->next = $this->createMock(RequestHandlerInterface::class); - $this->middleware = new DropDefaultDomainFromRequestMiddleware('s.test'); + $this->middleware = new DropDefaultDomainFromRequestMiddleware(new UrlShortenerOptions('s.test')); } #[Test, DataProvider('provideQueryParams')]