diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 38ccb54c..385066e6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- php-version: ['8.0']
+ php-version: ['8.1']
command: ['cs', 'stan', 'swagger:validate']
steps:
- name: Checkout code
@@ -32,7 +32,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- php-version: ['8.0', '8.1']
+ php-version: ['8.1']
test-group: ['unit', 'api']
steps:
- name: Checkout code
@@ -51,7 +51,7 @@ jobs:
- run: composer install --no-interaction --prefer-dist
- run: composer test:${{ matrix.test-group }}:ci
- uses: actions/upload-artifact@v2
- if: ${{ matrix.php-version == '8.0' }}
+ if: ${{ matrix.php-version == '8.1' }}
with:
name: coverage-${{ matrix.test-group }}
path: |
@@ -62,7 +62,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- php-version: ['8.0', '8.1']
+ php-version: ['8.1']
platform: ['sqlite:ci', 'mysql', 'maria', 'postgres', 'ms']
env:
LC_ALL: C
@@ -91,7 +91,7 @@ jobs:
run: composer test:db:${{ matrix.platform }}
- name: Upload code coverage
uses: actions/upload-artifact@v2
- if: ${{ matrix.php-version == '8.0' && matrix.platform == 'sqlite:ci' }}
+ if: ${{ matrix.php-version == '8.1' && matrix.platform == 'sqlite:ci' }}
with:
name: coverage-db
path: |
@@ -105,7 +105,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- php-version: ['8.0', '8.1']
+ php-version: ['8.1']
test-group: ['unit', 'db', 'api']
steps:
- name: Checkout code
@@ -136,7 +136,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- php-version: ['8.0']
+ php-version: ['8.1']
steps:
- name: Checkout code
uses: actions/checkout@v2
@@ -152,8 +152,8 @@ jobs:
- run: mv build/coverage-unit/coverage-unit.cov build/coverage-unit.cov
- run: mv build/coverage-db/coverage-db.cov build/coverage-db.cov
- run: mv build/coverage-api/coverage-api.cov build/coverage-api.cov
- - run: wget https://phar.phpunit.de/phpcov-8.2.0.phar
- - run: php phpcov-8.2.0.phar merge build --clover build/clover.xml
+ - run: wget https://phar.phpunit.de/phpcov-8.2.1.phar
+ - run: php phpcov-8.2.1.phar merge build --clover build/clover.xml
- name: Publish coverage
uses: codecov/codecov-action@v1
with:
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
index 6d50221a..f872ebee 100644
--- a/.github/workflows/publish-release.yml
+++ b/.github/workflows/publish-release.yml
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- php-version: ['8.0', '8.1']
+ php-version: ['8.1']
swoole: ['yes', 'no']
steps:
- name: Checkout code
@@ -53,8 +53,8 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- php-version: [ '8.0', '8.1' ]
- swoole: [ 'yes', 'no' ]
+ php-version: ['8.1']
+ swoole: ['yes', 'no']
steps:
- uses: geekyeggo/delete-artifact@v1
with:
diff --git a/.github/workflows/publish-swagger-spec.yml b/.github/workflows/publish-swagger-spec.yml
index 5b97ee6c..51907814 100644
--- a/.github/workflows/publish-swagger-spec.yml
+++ b/.github/workflows/publish-swagger-spec.yml
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
- php-version: ['8.0']
+ php-version: ['8.1']
steps:
- name: Checkout code
uses: actions/checkout@v2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 882bd2ed..c29a817e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
+## [Unreleased]
+### Added
+* *Nothing*
+
+### Changed
+* *Nothing*
+
+### Deprecated
+* *Nothing*
+
+### Removed
+* [#1280](https://github.com/shlinkio/shlink/issues/1280) Dropped support for PHP 8.0
+
+### Fixed
+* *Nothing*
+
+
## [3.1.0] - 2022-04-23
### Added
* [#1294](https://github.com/shlinkio/shlink/issues/1294) Allowed to provide a specific domain when importing URLs from YOURLS.
diff --git a/README.md b/README.md
index 6f4afd37..f72d9f92 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ The idea is that you can just generate a container using the image and provide t
First, make sure the host where you are going to run shlink fulfills these requirements:
-* PHP 8.0 or 8.1
+* PHP 8.1
* The next PHP extensions: json, curl, pdo, intl, gd and gmp/bcmath.
* apcu extension is recommended if you don't plan to use openswoole.
* xml extension is required if you want to generate QR codes in svg format.
diff --git a/composer.json b/composer.json
index 2446aace..1e8c580b 100644
--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,7 @@
}
],
"require": {
- "php": "^8.0",
+ "php": "^8.1",
"ext-json": "*",
"ext-pdo": "*",
"akrabat/ip-address-middleware": "^2.1",
diff --git a/config/autoload/delete_short_urls.global.php b/config/autoload/delete_short_urls.global.php
index 3d562f78..2d203ea1 100644
--- a/config/autoload/delete_short_urls.global.php
+++ b/config/autoload/delete_short_urls.global.php
@@ -7,7 +7,7 @@ namespace Shlinkio\Shlink;
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
- $threshold = EnvVars::DELETE_SHORT_URL_THRESHOLD()->loadFromEnv();
+ $threshold = EnvVars::DELETE_SHORT_URL_THRESHOLD->loadFromEnv();
return [
diff --git a/config/autoload/entity-manager.global.php b/config/autoload/entity-manager.global.php
index d98d37dc..5a75ca6b 100644
--- a/config/autoload/entity-manager.global.php
+++ b/config/autoload/entity-manager.global.php
@@ -8,7 +8,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
use function Functional\contains;
return (static function (): array {
- $driver = EnvVars::DB_DRIVER()->loadFromEnv();
+ $driver = EnvVars::DB_DRIVER->loadFromEnv();
$isMysqlCompatible = contains(['maria', 'mysql'], $driver);
$resolveDriver = static fn () => match ($driver) {
@@ -35,12 +35,12 @@ return (static function (): array {
],
default => [
'driver' => $resolveDriver(),
- 'dbname' => EnvVars::DB_NAME()->loadFromEnv('shlink'),
- 'user' => EnvVars::DB_USER()->loadFromEnv(),
- 'password' => EnvVars::DB_PASSWORD()->loadFromEnv(),
- 'host' => EnvVars::DB_HOST()->loadFromEnv(EnvVars::DB_UNIX_SOCKET()->loadFromEnv()),
- 'port' => EnvVars::DB_PORT()->loadFromEnv($resolveDefaultPort()),
- 'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET()->loadFromEnv() : null,
+ 'dbname' => EnvVars::DB_NAME->loadFromEnv('shlink'),
+ 'user' => EnvVars::DB_USER->loadFromEnv(),
+ 'password' => EnvVars::DB_PASSWORD->loadFromEnv(),
+ 'host' => EnvVars::DB_HOST->loadFromEnv(EnvVars::DB_UNIX_SOCKET->loadFromEnv()),
+ 'port' => EnvVars::DB_PORT->loadFromEnv($resolveDefaultPort()),
+ 'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null,
'charset' => $resolveCharset(),
],
};
diff --git a/config/autoload/geolite2.global.php b/config/autoload/geolite2.global.php
index cf1f57fc..b31cfc6d 100644
--- a/config/autoload/geolite2.global.php
+++ b/config/autoload/geolite2.global.php
@@ -9,7 +9,7 @@ return [
'geolite2' => [
'db_location' => __DIR__ . '/../../data/GeoLite2-City.mmdb',
'temp_dir' => __DIR__ . '/../../data',
- 'license_key' => EnvVars::GEOLITE_LICENSE_KEY()->loadFromEnv(),
+ 'license_key' => EnvVars::GEOLITE_LICENSE_KEY->loadFromEnv(),
],
];
diff --git a/config/autoload/locks.global.php b/config/autoload/locks.global.php
index bdbdb8e5..9b014496 100644
--- a/config/autoload/locks.global.php
+++ b/config/autoload/locks.global.php
@@ -24,7 +24,7 @@ return [
LOCAL_LOCK_FACTORY => ConfigAbstractFactory::class,
],
'aliases' => [
- 'lock_store' => EnvVars::REDIS_SERVERS()->existsInEnv() ? 'redis_lock_store' : 'local_lock_store',
+ 'lock_store' => EnvVars::REDIS_SERVERS->existsInEnv() ? 'redis_lock_store' : 'local_lock_store',
'redis_lock_store' => Lock\Store\RedisStore::class,
'local_lock_store' => Lock\Store\FlockStore::class,
diff --git a/config/autoload/mercure.global.php b/config/autoload/mercure.global.php
index ba261369..67143919 100644
--- a/config/autoload/mercure.global.php
+++ b/config/autoload/mercure.global.php
@@ -9,14 +9,14 @@ use Symfony\Component\Mercure\Hub;
use Symfony\Component\Mercure\HubInterface;
return (static function (): array {
- $publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL()->loadFromEnv();
+ $publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv();
return [
'mercure' => [
'public_hub_url' => $publicUrl,
- 'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL()->loadFromEnv($publicUrl),
- 'jwt_secret' => EnvVars::MERCURE_JWT_SECRET()->loadFromEnv(),
+ 'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv($publicUrl),
+ 'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(),
'jwt_issuer' => 'Shlink',
],
diff --git a/config/autoload/qr-codes.global.php b/config/autoload/qr-codes.global.php
index d72198af..dc4f5f9e 100644
--- a/config/autoload/qr-codes.global.php
+++ b/config/autoload/qr-codes.global.php
@@ -13,13 +13,13 @@ use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE;
return [
'qr_codes' => [
- 'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE()->loadFromEnv(DEFAULT_QR_CODE_SIZE),
- 'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN()->loadFromEnv(DEFAULT_QR_CODE_MARGIN),
- 'format' => EnvVars::DEFAULT_QR_CODE_FORMAT()->loadFromEnv(DEFAULT_QR_CODE_FORMAT),
- 'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION()->loadFromEnv(
+ 'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(DEFAULT_QR_CODE_SIZE),
+ 'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(DEFAULT_QR_CODE_MARGIN),
+ 'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(DEFAULT_QR_CODE_FORMAT),
+ 'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(
DEFAULT_QR_CODE_ERROR_CORRECTION,
),
- 'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE()->loadFromEnv(
+ 'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(
DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
),
],
diff --git a/config/autoload/rabbit.global.php b/config/autoload/rabbit.global.php
index faa5f569..a9764c8c 100644
--- a/config/autoload/rabbit.global.php
+++ b/config/autoload/rabbit.global.php
@@ -10,12 +10,12 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'rabbitmq' => [
- 'enabled' => (bool) EnvVars::RABBITMQ_ENABLED()->loadFromEnv(false),
- 'host' => EnvVars::RABBITMQ_HOST()->loadFromEnv(),
- 'port' => (int) EnvVars::RABBITMQ_PORT()->loadFromEnv('5672'),
- 'user' => EnvVars::RABBITMQ_USER()->loadFromEnv(),
- 'password' => EnvVars::RABBITMQ_PASSWORD()->loadFromEnv(),
- 'vhost' => EnvVars::RABBITMQ_VHOST()->loadFromEnv('/'),
+ 'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(false),
+ 'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(),
+ 'port' => (int) EnvVars::RABBITMQ_PORT->loadFromEnv('5672'),
+ 'user' => EnvVars::RABBITMQ_USER->loadFromEnv(),
+ 'password' => EnvVars::RABBITMQ_PASSWORD->loadFromEnv(),
+ 'vhost' => EnvVars::RABBITMQ_VHOST->loadFromEnv('/'),
],
'dependencies' => [
diff --git a/config/autoload/redirects.global.php b/config/autoload/redirects.global.php
index 08439b2a..426bb2ac 100644
--- a/config/autoload/redirects.global.php
+++ b/config/autoload/redirects.global.php
@@ -10,14 +10,14 @@ use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE;
return [
'not_found_redirects' => [
- '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(),
+ '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(DEFAULT_REDIRECT_STATUS_CODE),
- 'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME()->loadFromEnv(
+ 'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(DEFAULT_REDIRECT_STATUS_CODE),
+ 'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(
DEFAULT_REDIRECT_CACHE_LIFETIME,
),
],
diff --git a/config/autoload/redis.global.php b/config/autoload/redis.global.php
index f87d77f3..0133d1b1 100644
--- a/config/autoload/redis.global.php
+++ b/config/autoload/redis.global.php
@@ -5,7 +5,7 @@ declare(strict_types=1);
use Shlinkio\Shlink\Core\Config\EnvVars;
return (static function (): array {
- $redisServers = EnvVars::REDIS_SERVERS()->loadFromEnv();
+ $redisServers = EnvVars::REDIS_SERVERS->loadFromEnv();
return match ($redisServers) {
null => [],
@@ -13,7 +13,7 @@ return (static function (): array {
'cache' => [
'redis' => [
'servers' => $redisServers,
- 'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE()->loadFromEnv(),
+ 'sentinel_service' => EnvVars::REDIS_SENTINEL_SERVICE->loadFromEnv(),
],
],
],
diff --git a/config/autoload/router.global.php b/config/autoload/router.global.php
index fd1f9525..8b5e856e 100644
--- a/config/autoload/router.global.php
+++ b/config/autoload/router.global.php
@@ -8,7 +8,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
return [
'router' => [
- 'base_path' => EnvVars::BASE_PATH()->loadFromEnv(''),
+ 'base_path' => EnvVars::BASE_PATH->loadFromEnv(''),
'fastroute' => [
FastRouteRouter::CONFIG_CACHE_ENABLED => true,
diff --git a/config/autoload/swoole.global.php b/config/autoload/swoole.global.php
index 987c967e..36cba24f 100644
--- a/config/autoload/swoole.global.php
+++ b/config/autoload/swoole.global.php
@@ -7,7 +7,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
use const Shlinkio\Shlink\MIN_TASK_WORKERS;
return (static function (): array {
- $taskWorkers = (int) EnvVars::TASK_WORKER_NUM()->loadFromEnv(16);
+ $taskWorkers = (int) EnvVars::TASK_WORKER_NUM->loadFromEnv(16);
return [
@@ -17,11 +17,11 @@ return (static function (): array {
'swoole-http-server' => [
'host' => '0.0.0.0',
- 'port' => (int) EnvVars::PORT()->loadFromEnv(8080),
+ 'port' => (int) EnvVars::PORT->loadFromEnv(8080),
'process-name' => 'shlink',
'options' => [
- 'worker_num' => (int) EnvVars::WEB_WORKER_NUM()->loadFromEnv(16),
+ 'worker_num' => (int) EnvVars::WEB_WORKER_NUM->loadFromEnv(16),
'task_worker_num' => max($taskWorkers, MIN_TASK_WORKERS),
],
],
diff --git a/config/autoload/tracking.global.php b/config/autoload/tracking.global.php
index b2596830..0637301a 100644
--- a/config/autoload/tracking.global.php
+++ b/config/autoload/tracking.global.php
@@ -9,28 +9,28 @@ return [
'tracking' => [
// 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(true),
+ 'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR->loadFromEnv(true),
// 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(true),
+ 'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(true),
// A query param that, if provided, will disable tracking of one particular visit. Always takes precedence
- 'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM()->loadFromEnv(),
+ 'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(),
// If true, visits will not be tracked at all
- 'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING()->loadFromEnv(false),
+ 'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(false),
// 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(false),
+ 'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(false),
// If true, the referrer will not be tracked
- 'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING()->loadFromEnv(false),
+ 'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(false),
// If true, the user agent will not be tracked
- 'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING()->loadFromEnv(false),
+ 'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(false),
// A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default
- 'disable_tracking_from' => EnvVars::DISABLE_TRACKING_FROM()->loadFromEnv(),
+ 'disable_tracking_from' => EnvVars::DISABLE_TRACKING_FROM->loadFromEnv(),
],
];
diff --git a/config/autoload/url-shortener.global.php b/config/autoload/url-shortener.global.php
index 58c12f05..bdd257d5 100644
--- a/config/autoload/url-shortener.global.php
+++ b/config/autoload/url-shortener.global.php
@@ -9,7 +9,7 @@ use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH;
return (static function (): array {
$shortCodesLength = max(
- (int) EnvVars::DEFAULT_SHORT_CODES_LENGTH()->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
+ (int) EnvVars::DEFAULT_SHORT_CODES_LENGTH->loadFromEnv(DEFAULT_SHORT_CODES_LENGTH),
MIN_SHORT_CODES_LENGTH,
);
@@ -17,12 +17,12 @@ return (static function (): array {
'url_shortener' => [
'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain
- 'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED()->loadFromEnv(true)) ? 'https' : 'http',
- 'hostname' => EnvVars::DEFAULT_DOMAIN()->loadFromEnv(''),
+ 'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv(true)) ? 'https' : 'http',
+ 'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(''),
],
'default_short_codes_length' => $shortCodesLength,
- 'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES()->loadFromEnv(false),
- 'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH()->loadFromEnv(false),
+ 'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(false),
+ 'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(false),
],
];
diff --git a/config/autoload/webhooks.global.php b/config/autoload/webhooks.global.php
index 5de7c53b..e72c4904 100644
--- a/config/autoload/webhooks.global.php
+++ b/config/autoload/webhooks.global.php
@@ -6,14 +6,14 @@ use Shlinkio\Shlink\Core\Config\EnvVars;
// Deprecated. Webhooks are no longer supported. To be removed in Shlink 4.0.0
return (static function (): array {
- $webhooks = EnvVars::VISITS_WEBHOOKS()->loadFromEnv();
+ $webhooks = EnvVars::VISITS_WEBHOOKS->loadFromEnv();
return [
'visits_webhooks' => [
'webhooks' => $webhooks === null ? [] : explode(',', $webhooks),
'notify_orphan_visits_to_webhooks' =>
- (bool) EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS()->loadFromEnv(false),
+ (bool) EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS->loadFromEnv(false),
],
];
diff --git a/config/container.php b/config/container.php
index 074502cd..6e95e84d 100644
--- a/config/container.php
+++ b/config/container.php
@@ -13,7 +13,7 @@ chdir(dirname(__DIR__));
require 'vendor/autoload.php';
// This is one of the first files loaded. Configure the timezone here
-date_default_timezone_set(EnvVars::TIMEZONE()->loadFromEnv(date_default_timezone_get()));
+date_default_timezone_set(EnvVars::TIMEZONE->loadFromEnv(date_default_timezone_get()));
// This class alias tricks the ConfigAbstractFactory to return Lock\Factory instances even with a different service name
// It needs to be placed here as individual config files will not be loaded once config is cached
diff --git a/data/infra/examples/nginx-vhost.conf b/data/infra/examples/nginx-vhost.conf
index 80ff8afd..5e05481a 100644
--- a/data/infra/examples/nginx-vhost.conf
+++ b/data/infra/examples/nginx-vhost.conf
@@ -11,7 +11,7 @@ server {
location ~ \.php$ {
fastcgi_split_path_info ^(.+\.php)(/.+)$;
- fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
+ fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
include fastcgi.conf;
}
diff --git a/data/migrations/Version20210207100807.php b/data/migrations/Version20210207100807.php
index 706132cc..cd0b0b12 100644
--- a/data/migrations/Version20210207100807.php
+++ b/data/migrations/Version20210207100807.php
@@ -8,8 +8,8 @@ use Doctrine\DBAL\Platforms\MySQLPlatform;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Types;
use Doctrine\Migrations\AbstractMigration;
-use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\Visitor;
+use Shlinkio\Shlink\Core\Visit\Model\VisitType;
final class Version20210207100807 extends AbstractMigration
{
@@ -27,7 +27,7 @@ final class Version20210207100807 extends AbstractMigration
]);
$visits->addColumn('type', Types::STRING, [
'length' => 255,
- 'default' => Visit::TYPE_VALID_SHORT_URL,
+ 'default' => VisitType::VALID_SHORT_URL->value,
]);
}
diff --git a/module/CLI/src/Command/Api/GenerateKeyCommand.php b/module/CLI/src/Command/Api/GenerateKeyCommand.php
index 2655d1fb..b24619ef 100644
--- a/module/CLI/src/Command/Api/GenerateKeyCommand.php
+++ b/module/CLI/src/Command/Api/GenerateKeyCommand.php
@@ -73,13 +73,16 @@ class GenerateKeyCommand extends Command
$authorOnly,
'a',
InputOption::VALUE_NONE,
- sprintf('Adds the "%s" role to the new API key.', Role::AUTHORED_SHORT_URLS),
+ sprintf('Adds the "%s" role to the new API key.', Role::AUTHORED_SHORT_URLS->value),
)
->addOption(
$domainOnly,
'd',
InputOption::VALUE_REQUIRED,
- sprintf('Adds the "%s" role to the new API key, with the domain provided.', Role::DOMAIN_SPECIFIC),
+ sprintf(
+ 'Adds the "%s" role to the new API key, with the domain provided.',
+ Role::DOMAIN_SPECIFIC->value,
+ ),
)
->setHelp($help);
}
@@ -99,7 +102,7 @@ class GenerateKeyCommand extends Command
if (! $apiKey->isAdmin()) {
ShlinkTable::default($io)->render(
['Role name', 'Role metadata'],
- $apiKey->mapRoles(fn (string $name, array $meta) => [$name, arrayToString($meta, 0)]),
+ $apiKey->mapRoles(fn (Role $role, array $meta) => [$role->value, arrayToString($meta, 0)]),
null,
'Roles',
);
diff --git a/module/CLI/src/Command/Api/ListKeysCommand.php b/module/CLI/src/Command/Api/ListKeysCommand.php
index 0a331086..0e98af31 100644
--- a/module/CLI/src/Command/Api/ListKeysCommand.php
+++ b/module/CLI/src/Command/Api/ListKeysCommand.php
@@ -60,10 +60,10 @@ class ListKeysCommand extends Command
}
$rowData[] = $expiration?->toAtomString() ?? '-';
$rowData[] = $apiKey->isAdmin() ? 'Admin' : implode("\n", $apiKey->mapRoles(
- fn (string $roleName, array $meta) =>
+ fn (Role $role, array $meta) =>
empty($meta)
- ? Role::toFriendlyName($roleName)
- : sprintf('%s: %s', Role::toFriendlyName($roleName), Role::domainAuthorityFromMeta($meta)),
+ ? Role::toFriendlyName($role)
+ : sprintf('%s: %s', Role::toFriendlyName($role), Role::domainAuthorityFromMeta($meta)),
));
return $rowData;
diff --git a/module/CLI/src/Command/Domain/DomainRedirectsCommand.php b/module/CLI/src/Command/Domain/DomainRedirectsCommand.php
index 90cfd1f7..c546fd5b 100644
--- a/module/CLI/src/Command/Domain/DomainRedirectsCommand.php
+++ b/module/CLI/src/Command/Domain/DomainRedirectsCommand.php
@@ -53,7 +53,7 @@ class DomainRedirectsCommand extends Command
/** @var string[] $availableDomains */
$availableDomains = invoke(
- filter($this->domainService->listDomains(), static fn (DomainItem $item) => ! $item->isDefault()),
+ filter($this->domainService->listDomains(), static fn (DomainItem $item) => ! $item->isDefault),
'toString',
);
if (empty($availableDomains)) {
diff --git a/module/CLI/src/Command/Domain/ListDomainsCommand.php b/module/CLI/src/Command/Domain/ListDomainsCommand.php
index 447bf92f..8f2ee22c 100644
--- a/module/CLI/src/Command/Domain/ListDomainsCommand.php
+++ b/module/CLI/src/Command/Domain/ListDomainsCommand.php
@@ -48,12 +48,12 @@ class ListDomainsCommand extends Command
$table->render(
$showRedirects ? [...$commonFields, '"Not found" redirects'] : $commonFields,
map($domains, function (DomainItem $domain) use ($showRedirects) {
- $commonValues = [$domain->toString(), $domain->isDefault() ? 'Yes' : 'No'];
+ $commonValues = [$domain->toString(), $domain->isDefault ? 'Yes' : 'No'];
return $showRedirects
? [
...$commonValues,
- $this->notFoundRedirectsToString($domain->notFoundRedirectConfig()),
+ $this->notFoundRedirectsToString($domain->notFoundRedirectConfig),
]
: $commonValues;
}),
diff --git a/module/CLI/src/Command/ShortUrl/DeleteShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/DeleteShortUrlCommand.php
index fc4e8331..db1b1dfd 100644
--- a/module/CLI/src/Command/ShortUrl/DeleteShortUrlCommand.php
+++ b/module/CLI/src/Command/ShortUrl/DeleteShortUrlCommand.php
@@ -81,6 +81,6 @@ class DeleteShortUrlCommand extends Command
private function runDelete(SymfonyStyle $io, ShortUrlIdentifier $identifier, bool $ignoreThreshold): void
{
$this->deleteShortUrlService->deleteByShortCode($identifier, $ignoreThreshold);
- $io->success(sprintf('Short URL with short code "%s" successfully deleted.', $identifier->shortCode()));
+ $io->success(sprintf('Short URL with short code "%s" successfully deleted.', $identifier->shortCode));
}
}
diff --git a/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php b/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php
index ebc9e783..fc0f19a0 100644
--- a/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php
+++ b/module/CLI/src/Command/ShortUrl/ListShortUrlsCommand.php
@@ -13,6 +13,7 @@ use Shlinkio\Shlink\Common\Rest\DataTransformerInterface;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
+use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
use Shlinkio\Shlink\Core\Validation\ShortUrlsParamsInputFilter;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -120,9 +121,7 @@ class ListShortUrlsCommand extends AbstractWithDateRangeCommand
$page = (int) $input->getOption('page');
$searchTerm = $input->getOption('search-term');
$tags = $input->getOption('tags');
- $tagsMode = $input->getOption('including-all-tags') === true
- ? ShortUrlsParams::TAGS_MODE_ALL
- : ShortUrlsParams::TAGS_MODE_ANY;
+ $tagsMode = $input->getOption('including-all-tags') === true ? TagsMode::ALL->value : TagsMode::ANY->value;
$tags = ! empty($tags) ? explode(',', $tags) : [];
$all = $input->getOption('all');
$startDate = $this->getStartDateOption($input, $output);
diff --git a/module/CLI/src/Command/Tag/ListTagsCommand.php b/module/CLI/src/Command/Tag/ListTagsCommand.php
index 9c7269fa..cd820169 100644
--- a/module/CLI/src/Command/Tag/ListTagsCommand.php
+++ b/module/CLI/src/Command/Tag/ListTagsCommand.php
@@ -46,7 +46,7 @@ class ListTagsCommand extends Command
return map(
$tags,
- static fn (TagInfo $tagInfo) => [$tagInfo->tag(), $tagInfo->shortUrlsCount(), $tagInfo->visitsCount()],
+ static fn (TagInfo $tagInfo) => [$tagInfo->tag, $tagInfo->shortUrlsCount, $tagInfo->visitsCount],
);
}
}
diff --git a/module/CLI/src/Command/Util/AbstractLockedCommand.php b/module/CLI/src/Command/Util/AbstractLockedCommand.php
index 9482694b..5f3a33ea 100644
--- a/module/CLI/src/Command/Util/AbstractLockedCommand.php
+++ b/module/CLI/src/Command/Util/AbstractLockedCommand.php
@@ -22,11 +22,11 @@ abstract class AbstractLockedCommand extends Command
final protected function execute(InputInterface $input, OutputInterface $output): ?int
{
$lockConfig = $this->getLockConfig();
- $lock = $this->locker->createLock($lockConfig->lockName(), $lockConfig->ttl(), $lockConfig->isBlocking());
+ $lock = $this->locker->createLock($lockConfig->lockName, $lockConfig->ttl, $lockConfig->isBlocking);
- if (! $lock->acquire($lockConfig->isBlocking())) {
+ if (! $lock->acquire($lockConfig->isBlocking)) {
$output->writeln(
- sprintf('Command "%s" is already in progress. Skipping.', $lockConfig->lockName()),
+ sprintf('Command "%s" is already in progress. Skipping.', $lockConfig->lockName),
);
return ExitCodes::EXIT_WARNING;
}
diff --git a/module/CLI/src/Command/Util/LockedCommandConfig.php b/module/CLI/src/Command/Util/LockedCommandConfig.php
index f053d99a..8e357329 100644
--- a/module/CLI/src/Command/Util/LockedCommandConfig.php
+++ b/module/CLI/src/Command/Util/LockedCommandConfig.php
@@ -9,9 +9,9 @@ final class LockedCommandConfig
public const DEFAULT_TTL = 600.0; // 10 minutes
private function __construct(
- private string $lockName,
- private bool $isBlocking,
- private float $ttl = self::DEFAULT_TTL,
+ public readonly string $lockName,
+ public readonly bool $isBlocking,
+ public readonly float $ttl = self::DEFAULT_TTL,
) {
}
@@ -24,19 +24,4 @@ final class LockedCommandConfig
{
return new self($lockName, false);
}
-
- public function lockName(): string
- {
- return $this->lockName;
- }
-
- public function isBlocking(): bool
- {
- return $this->isBlocking;
- }
-
- public function ttl(): float
- {
- return $this->ttl;
- }
}
diff --git a/module/CLI/src/Exception/InvalidRoleConfigException.php b/module/CLI/src/Exception/InvalidRoleConfigException.php
index 51adb234..ae483766 100644
--- a/module/CLI/src/Exception/InvalidRoleConfigException.php
+++ b/module/CLI/src/Exception/InvalidRoleConfigException.php
@@ -16,7 +16,7 @@ class InvalidRoleConfigException extends InvalidArgumentException implements Exc
return new self(sprintf(
'You cannot create an API key with the "%s" role attached to the default domain. '
. 'The role is currently limited to non-default domains.',
- Role::DOMAIN_SPECIFIC,
+ Role::DOMAIN_SPECIFIC->value,
));
}
}
diff --git a/module/CLI/src/Util/ShlinkTable.php b/module/CLI/src/Util/ShlinkTable.php
index 1d4143c1..cd38e5cd 100644
--- a/module/CLI/src/Util/ShlinkTable.php
+++ b/module/CLI/src/Util/ShlinkTable.php
@@ -15,7 +15,7 @@ final class ShlinkTable
private const DEFAULT_STYLE_NAME = 'default';
private const TABLE_TITLE_STYLE = ' %s >';
- private function __construct(private Table $baseTable, private bool $withRowSeparators)
+ private function __construct(private readonly Table $baseTable, private readonly bool $withRowSeparators)
{
}
diff --git a/module/CLI/test/Command/ShortUrl/DeleteShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/DeleteShortUrlCommandTest.php
index 10a363c7..947b7443 100644
--- a/module/CLI/test/Command/ShortUrl/DeleteShortUrlCommandTest.php
+++ b/module/CLI/test/Command/ShortUrl/DeleteShortUrlCommandTest.php
@@ -36,10 +36,11 @@ class DeleteShortUrlCommandTest extends TestCase
public function successMessageIsPrintedIfUrlIsProperlyDeleted(): void
{
$shortCode = 'abc123';
- $deleteByShortCode = $this->service->deleteByShortCode(new ShortUrlIdentifier($shortCode), false)->will(
- function (): void {
- },
- );
+ $deleteByShortCode = $this->service->deleteByShortCode(
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
+ false,
+ )->will(function (): void {
+ });
$this->commandTester->execute(['shortCode' => $shortCode]);
$output = $this->commandTester->getDisplay();
@@ -55,7 +56,7 @@ class DeleteShortUrlCommandTest extends TestCase
public function invalidShortCodePrintsMessage(): void
{
$shortCode = 'abc123';
- $identifier = new ShortUrlIdentifier($shortCode);
+ $identifier = ShortUrlIdentifier::fromShortCodeAndDomain($shortCode);
$deleteByShortCode = $this->service->deleteByShortCode($identifier, false)->willThrow(
Exception\ShortUrlNotFoundException::fromNotFound($identifier),
);
@@ -77,7 +78,7 @@ class DeleteShortUrlCommandTest extends TestCase
string $expectedMessage,
): void {
$shortCode = 'abc123';
- $identifier = new ShortUrlIdentifier($shortCode);
+ $identifier = ShortUrlIdentifier::fromShortCodeAndDomain($shortCode);
$deleteByShortCode = $this->service->deleteByShortCode($identifier, Argument::type('bool'))->will(
function (array $args) use ($shortCode): void {
$ignoreThreshold = array_pop($args);
@@ -114,12 +115,13 @@ class DeleteShortUrlCommandTest extends TestCase
public function deleteIsNotRetriedWhenThresholdIsReachedAndQuestionIsDeclined(): void
{
$shortCode = 'abc123';
- $deleteByShortCode = $this->service->deleteByShortCode(new ShortUrlIdentifier($shortCode), false)->willThrow(
- Exception\DeleteShortUrlException::fromVisitsThreshold(
- 10,
- ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
- ),
- );
+ $deleteByShortCode = $this->service->deleteByShortCode(
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
+ false,
+ )->willThrow(Exception\DeleteShortUrlException::fromVisitsThreshold(
+ 10,
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
+ ));
$this->commandTester->setInputs(['no']);
$this->commandTester->execute(['shortCode' => $shortCode]);
diff --git a/module/CLI/test/Command/ShortUrl/GetVisitsCommandTest.php b/module/CLI/test/Command/ShortUrl/GetVisitsCommandTest.php
index ca9e0981..7a884c89 100644
--- a/module/CLI/test/Command/ShortUrl/GetVisitsCommandTest.php
+++ b/module/CLI/test/Command/ShortUrl/GetVisitsCommandTest.php
@@ -44,7 +44,7 @@ class GetVisitsCommandTest extends TestCase
{
$shortCode = 'abc123';
$this->visitsHelper->visitsForShortUrl(
- new ShortUrlIdentifier($shortCode),
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
new VisitsParams(DateRange::emptyInstance()),
)
->willReturn(new Paginator(new ArrayAdapter([])))
@@ -60,7 +60,7 @@ class GetVisitsCommandTest extends TestCase
$startDate = '2016-01-01';
$endDate = '2016-02-01';
$this->visitsHelper->visitsForShortUrl(
- new ShortUrlIdentifier($shortCode),
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
new VisitsParams(DateRange::withStartAndEndDate(Chronos::parse($startDate), Chronos::parse($endDate))),
)
->willReturn(new Paginator(new ArrayAdapter([])))
@@ -79,7 +79,7 @@ class GetVisitsCommandTest extends TestCase
$shortCode = 'abc123';
$startDate = 'foo';
$info = $this->visitsHelper->visitsForShortUrl(
- new ShortUrlIdentifier($shortCode),
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
new VisitsParams(DateRange::emptyInstance()),
)->willReturn(new Paginator(new ArrayAdapter([])));
@@ -100,7 +100,10 @@ class GetVisitsCommandTest extends TestCase
public function outputIsProperlyGenerated(): void
{
$shortCode = 'abc123';
- $this->visitsHelper->visitsForShortUrl(new ShortUrlIdentifier($shortCode), Argument::any())->willReturn(
+ $this->visitsHelper->visitsForShortUrl(
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
+ Argument::any(),
+ )->willReturn(
new Paginator(new ArrayAdapter([
Visit::forValidShortUrl(ShortUrl::createEmpty(), new Visitor('bar', 'foo', '', ''))->locate(
VisitLocation::fromGeolocation(new Location('', 'Spain', '', '', 0, 0, '')),
diff --git a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php
index 38d3bcd3..f9d701cb 100644
--- a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php
+++ b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php
@@ -16,6 +16,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier;
+use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer;
use Shlinkio\Shlink\Rest\ApiKey\Model\ApiKeyMeta;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
@@ -205,23 +206,23 @@ class ListShortUrlsCommandTest extends TestCase
public function provideArgs(): iterable
{
- yield [[], 1, null, [], ShortUrlsParams::TAGS_MODE_ANY];
- yield [['--page' => $page = 3], $page, null, [], ShortUrlsParams::TAGS_MODE_ANY];
- yield [['--including-all-tags' => true], 1, null, [], ShortUrlsParams::TAGS_MODE_ALL];
- yield [['--search-term' => $searchTerm = 'search this'], 1, $searchTerm, [], ShortUrlsParams::TAGS_MODE_ANY];
+ yield [[], 1, null, [], TagsMode::ANY->value];
+ yield [['--page' => $page = 3], $page, null, [], TagsMode::ANY->value];
+ yield [['--including-all-tags' => true], 1, null, [], TagsMode::ALL->value];
+ yield [['--search-term' => $searchTerm = 'search this'], 1, $searchTerm, [], TagsMode::ANY->value];
yield [
['--page' => $page = 3, '--search-term' => $searchTerm = 'search this', '--tags' => $tags = 'foo,bar'],
$page,
$searchTerm,
explode(',', $tags),
- ShortUrlsParams::TAGS_MODE_ANY,
+ TagsMode::ANY->value,
];
yield [
['--start-date' => $startDate = '2019-01-01'],
1,
null,
[],
- ShortUrlsParams::TAGS_MODE_ANY,
+ TagsMode::ANY->value,
$startDate,
];
yield [
@@ -229,7 +230,7 @@ class ListShortUrlsCommandTest extends TestCase
1,
null,
[],
- ShortUrlsParams::TAGS_MODE_ANY,
+ TagsMode::ANY->value,
null,
$endDate,
];
@@ -238,7 +239,7 @@ class ListShortUrlsCommandTest extends TestCase
1,
null,
[],
- ShortUrlsParams::TAGS_MODE_ANY,
+ TagsMode::ANY->value,
$startDate,
$endDate,
];
@@ -276,7 +277,7 @@ class ListShortUrlsCommandTest extends TestCase
'page' => 1,
'searchTerm' => null,
'tags' => [],
- 'tagsMode' => ShortUrlsParams::TAGS_MODE_ANY,
+ 'tagsMode' => TagsMode::ANY->value,
'startDate' => null,
'endDate' => null,
'orderBy' => null,
diff --git a/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php
index 2a816207..12e29eaf 100644
--- a/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php
+++ b/module/CLI/test/Command/ShortUrl/ResolveUrlCommandTest.php
@@ -37,8 +37,9 @@ class ResolveUrlCommandTest extends TestCase
$shortCode = 'abc123';
$expectedUrl = 'http://domain.com/foo/bar';
$shortUrl = ShortUrl::withLongUrl($expectedUrl);
- $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode))->willReturn($shortUrl)
- ->shouldBeCalledOnce();
+ $this->urlResolver->resolveShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode))->willReturn(
+ $shortUrl,
+ )->shouldBeCalledOnce();
$this->commandTester->execute(['shortCode' => $shortCode]);
$output = $this->commandTester->getDisplay();
@@ -48,8 +49,8 @@ class ResolveUrlCommandTest extends TestCase
/** @test */
public function incorrectShortCodeOutputsErrorMessage(): void
{
- $identifier = new ShortUrlIdentifier('abc123');
- $shortCode = $identifier->shortCode();
+ $identifier = ShortUrlIdentifier::fromShortCodeAndDomain('abc123');
+ $shortCode = $identifier->shortCode;
$this->urlResolver->resolveShortUrl($identifier)
->willThrow(ShortUrlNotFoundException::fromNotFound($identifier))
diff --git a/module/CLI/test/Exception/InvalidRoleConfigExceptionTest.php b/module/CLI/test/Exception/InvalidRoleConfigExceptionTest.php
index 3b89b505..99c66ea4 100644
--- a/module/CLI/test/Exception/InvalidRoleConfigExceptionTest.php
+++ b/module/CLI/test/Exception/InvalidRoleConfigExceptionTest.php
@@ -20,7 +20,7 @@ class InvalidRoleConfigExceptionTest extends TestCase
self::assertEquals(sprintf(
'You cannot create an API key with the "%s" role attached to the default domain. '
. 'The role is currently limited to non-default domains.',
- Role::DOMAIN_SPECIFIC,
+ Role::DOMAIN_SPECIFIC->value,
), $e->getMessage());
}
}
diff --git a/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Visit.php b/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Visit.php
index 969bfd1d..147c37e7 100644
--- a/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Visit.php
+++ b/module/Core/config/entities-mappings/Shlinkio.Shlink.Core.Entity.Visit.php
@@ -6,9 +6,11 @@ namespace Shlinkio\Shlink\Core;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
+use Doctrine\ORM\Mapping\Builder\FieldBuilder;
use Doctrine\ORM\Mapping\ClassMetadata;
use Shlinkio\Shlink\Common\Doctrine\Type\ChronosDateTimeType;
use Shlinkio\Shlink\Core\Model\Visitor;
+use Shlinkio\Shlink\Core\Visit\Model\VisitType;
return static function (ClassMetadata $metadata, array $emConfig): void {
$builder = new ClassMetadataBuilder($metadata);
@@ -61,10 +63,13 @@ return static function (ClassMetadata $metadata, array $emConfig): void {
->nullable()
->build();
- $builder->createField('type', Types::STRING)
- ->columnName('type')
- ->length(255)
- ->build();
+ (new FieldBuilder($builder, [
+ 'fieldName' => 'type',
+ 'type' => Types::STRING,
+ 'enumType' => VisitType::class,
+ ]))->columnName('type')
+ ->length(255)
+ ->build();
$builder->createField('potentialBot', Types::BOOLEAN)
->columnName('potential_bot')
diff --git a/module/Core/src/Action/Model/QrCodeParams.php b/module/Core/src/Action/Model/QrCodeParams.php
index 42d643d3..7c1f0e34 100644
--- a/module/Core/src/Action/Model/QrCodeParams.php
+++ b/module/Core/src/Action/Model/QrCodeParams.php
@@ -29,11 +29,11 @@ final class QrCodeParams
private const SUPPORTED_FORMATS = ['png', 'svg'];
private function __construct(
- private int $size,
- private int $margin,
- private WriterInterface $writer,
- private ErrorCorrectionLevelInterface $errorCorrectionLevel,
- private RoundBlockSizeModeInterface $roundBlockSizeMode,
+ public readonly int $size,
+ public readonly int $margin,
+ public readonly WriterInterface $writer,
+ public readonly ErrorCorrectionLevelInterface $errorCorrectionLevel,
+ public readonly RoundBlockSizeModeInterface $roundBlockSizeMode,
) {
}
@@ -105,29 +105,4 @@ final class QrCodeParams
{
return strtolower(trim($param));
}
-
- public function size(): int
- {
- return $this->size;
- }
-
- public function margin(): int
- {
- return $this->margin;
- }
-
- public function writer(): WriterInterface
- {
- return $this->writer;
- }
-
- public function errorCorrectionLevel(): ErrorCorrectionLevelInterface
- {
- return $this->errorCorrectionLevel;
- }
-
- public function roundBlockSizeMode(): RoundBlockSizeModeInterface
- {
- return $this->roundBlockSizeMode;
- }
}
diff --git a/module/Core/src/Action/QrCodeAction.php b/module/Core/src/Action/QrCodeAction.php
index 7772a5c8..17bdbdfd 100644
--- a/module/Core/src/Action/QrCodeAction.php
+++ b/module/Core/src/Action/QrCodeAction.php
@@ -42,11 +42,11 @@ class QrCodeAction implements MiddlewareInterface
$params = QrCodeParams::fromRequest($request, $this->defaultOptions);
$qrCodeBuilder = Builder::create()
->data($this->stringifier->stringify($shortUrl))
- ->size($params->size())
- ->margin($params->margin())
- ->writer($params->writer())
- ->errorCorrectionLevel($params->errorCorrectionLevel())
- ->roundBlockSizeMode($params->roundBlockSizeMode());
+ ->size($params->size)
+ ->margin($params->margin)
+ ->writer($params->writer)
+ ->errorCorrectionLevel($params->errorCorrectionLevel)
+ ->roundBlockSizeMode($params->roundBlockSizeMode);
return new QrCodeResponse($qrCodeBuilder->build());
}
diff --git a/module/Core/src/Config/EnvVars.php b/module/Core/src/Config/EnvVars.php
index 112b7599..e26e3097 100644
--- a/module/Core/src/Config/EnvVars.php
+++ b/module/Core/src/Config/EnvVars.php
@@ -2,155 +2,70 @@
declare(strict_types=1);
+// phpcs:disable
+// TODO Enable coding style checks again once code sniffer 3.7 is released https://github.com/squizlabs/PHP_CodeSniffer/issues/3474
namespace Shlinkio\Shlink\Core\Config;
-use ReflectionClass;
-use ReflectionClassConstant;
-use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
-
-use function array_values;
-use function Functional\contains;
use function Shlinkio\Shlink\Config\env;
-// TODO Convert to enum after dropping PHP 8.0 support
-
-/**
- * @method static EnvVars DELETE_SHORT_URL_THRESHOLD()
- * @method static EnvVars DB_DRIVER()
- * @method static EnvVars DB_NAME()
- * @method static EnvVars DB_USER()
- * @method static EnvVars DB_PASSWORD()
- * @method static EnvVars DB_HOST()
- * @method static EnvVars DB_UNIX_SOCKET()
- * @method static EnvVars DB_PORT()
- * @method static EnvVars GEOLITE_LICENSE_KEY()
- * @method static EnvVars REDIS_SERVERS()
- * @method static EnvVars REDIS_SENTINEL_SERVICE()
- * @method static EnvVars MERCURE_PUBLIC_HUB_URL()
- * @method static EnvVars MERCURE_INTERNAL_HUB_URL()
- * @method static EnvVars MERCURE_JWT_SECRET()
- * @method static EnvVars DEFAULT_QR_CODE_SIZE()
- * @method static EnvVars DEFAULT_QR_CODE_MARGIN()
- * @method static EnvVars DEFAULT_QR_CODE_FORMAT()
- * @method static EnvVars DEFAULT_QR_CODE_ERROR_CORRECTION()
- * @method static EnvVars DEFAULT_QR_CODE_ROUND_BLOCK_SIZE()
- * @method static EnvVars RABBITMQ_ENABLED()
- * @method static EnvVars RABBITMQ_HOST()
- * @method static EnvVars RABBITMQ_PORT()
- * @method static EnvVars RABBITMQ_USER()
- * @method static EnvVars RABBITMQ_PASSWORD()
- * @method static EnvVars RABBITMQ_VHOST()
- * @method static EnvVars DEFAULT_INVALID_SHORT_URL_REDIRECT()
- * @method static EnvVars DEFAULT_REGULAR_404_REDIRECT()
- * @method static EnvVars DEFAULT_BASE_URL_REDIRECT()
- * @method static EnvVars REDIRECT_STATUS_CODE()
- * @method static EnvVars REDIRECT_CACHE_LIFETIME()
- * @method static EnvVars BASE_PATH()
- * @method static EnvVars PORT()
- * @method static EnvVars TASK_WORKER_NUM()
- * @method static EnvVars WEB_WORKER_NUM()
- * @method static EnvVars ANONYMIZE_REMOTE_ADDR()
- * @method static EnvVars TRACK_ORPHAN_VISITS()
- * @method static EnvVars DISABLE_TRACK_PARAM()
- * @method static EnvVars DISABLE_TRACKING()
- * @method static EnvVars DISABLE_IP_TRACKING()
- * @method static EnvVars DISABLE_REFERRER_TRACKING()
- * @method static EnvVars DISABLE_UA_TRACKING()
- * @method static EnvVars DISABLE_TRACKING_FROM()
- * @method static EnvVars DEFAULT_SHORT_CODES_LENGTH()
- * @method static EnvVars IS_HTTPS_ENABLED()
- * @method static EnvVars DEFAULT_DOMAIN()
- * @method static EnvVars AUTO_RESOLVE_TITLES()
- * @method static EnvVars REDIRECT_APPEND_EXTRA_PATH()
- * @method static EnvVars TIMEZONE()
- * @method static EnvVars VISITS_WEBHOOKS()
- * @method static EnvVars NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS()
- */
-final class EnvVars
+enum EnvVars: string
{
- public const DELETE_SHORT_URL_THRESHOLD = 'DELETE_SHORT_URL_THRESHOLD';
- public const DB_DRIVER = 'DB_DRIVER';
- public const DB_NAME = 'DB_NAME';
- public const DB_USER = 'DB_USER';
- public const DB_PASSWORD = 'DB_PASSWORD';
- public const DB_HOST = 'DB_HOST';
- public const DB_UNIX_SOCKET = 'DB_UNIX_SOCKET';
- public const DB_PORT = 'DB_PORT';
- public const GEOLITE_LICENSE_KEY = 'GEOLITE_LICENSE_KEY';
- public const REDIS_SERVERS = 'REDIS_SERVERS';
- public const REDIS_SENTINEL_SERVICE = 'REDIS_SENTINEL_SERVICE';
- public const MERCURE_PUBLIC_HUB_URL = 'MERCURE_PUBLIC_HUB_URL';
- public const MERCURE_INTERNAL_HUB_URL = 'MERCURE_INTERNAL_HUB_URL';
- public const MERCURE_JWT_SECRET = 'MERCURE_JWT_SECRET';
- public const DEFAULT_QR_CODE_SIZE = 'DEFAULT_QR_CODE_SIZE';
- public const DEFAULT_QR_CODE_MARGIN = 'DEFAULT_QR_CODE_MARGIN';
- public const DEFAULT_QR_CODE_FORMAT = 'DEFAULT_QR_CODE_FORMAT';
- public const DEFAULT_QR_CODE_ERROR_CORRECTION = 'DEFAULT_QR_CODE_ERROR_CORRECTION';
- public const DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = 'DEFAULT_QR_CODE_ROUND_BLOCK_SIZE';
- public const RABBITMQ_ENABLED = 'RABBITMQ_ENABLED';
- public const RABBITMQ_HOST = 'RABBITMQ_HOST';
- public const RABBITMQ_PORT = 'RABBITMQ_PORT';
- public const RABBITMQ_USER = 'RABBITMQ_USER';
- public const RABBITMQ_PASSWORD = 'RABBITMQ_PASSWORD';
- public const RABBITMQ_VHOST = 'RABBITMQ_VHOST';
- public const DEFAULT_INVALID_SHORT_URL_REDIRECT = 'DEFAULT_INVALID_SHORT_URL_REDIRECT';
- public const DEFAULT_REGULAR_404_REDIRECT = 'DEFAULT_REGULAR_404_REDIRECT';
- public const DEFAULT_BASE_URL_REDIRECT = 'DEFAULT_BASE_URL_REDIRECT';
- public const REDIRECT_STATUS_CODE = 'REDIRECT_STATUS_CODE';
- public const REDIRECT_CACHE_LIFETIME = 'REDIRECT_CACHE_LIFETIME';
- public const BASE_PATH = 'BASE_PATH';
- public const PORT = 'PORT';
- public const TASK_WORKER_NUM = 'TASK_WORKER_NUM';
- public const WEB_WORKER_NUM = 'WEB_WORKER_NUM';
- public const ANONYMIZE_REMOTE_ADDR = 'ANONYMIZE_REMOTE_ADDR';
- public const TRACK_ORPHAN_VISITS = 'TRACK_ORPHAN_VISITS';
- public const DISABLE_TRACK_PARAM = 'DISABLE_TRACK_PARAM';
- public const DISABLE_TRACKING = 'DISABLE_TRACKING';
- public const DISABLE_IP_TRACKING = 'DISABLE_IP_TRACKING';
- public const DISABLE_REFERRER_TRACKING = 'DISABLE_REFERRER_TRACKING';
- public const DISABLE_UA_TRACKING = 'DISABLE_UA_TRACKING';
- public const DISABLE_TRACKING_FROM = 'DISABLE_TRACKING_FROM';
- public const DEFAULT_SHORT_CODES_LENGTH = 'DEFAULT_SHORT_CODES_LENGTH';
- public const IS_HTTPS_ENABLED = 'IS_HTTPS_ENABLED';
- public const DEFAULT_DOMAIN = 'DEFAULT_DOMAIN';
- public const AUTO_RESOLVE_TITLES = 'AUTO_RESOLVE_TITLES';
- public const REDIRECT_APPEND_EXTRA_PATH = 'REDIRECT_APPEND_EXTRA_PATH';
- public const TIMEZONE = 'TIMEZONE';
+ case DELETE_SHORT_URL_THRESHOLD = 'DELETE_SHORT_URL_THRESHOLD';
+ case DB_DRIVER = 'DB_DRIVER';
+ case DB_NAME = 'DB_NAME';
+ case DB_USER = 'DB_USER';
+ case DB_PASSWORD = 'DB_PASSWORD';
+ case DB_HOST = 'DB_HOST';
+ case DB_UNIX_SOCKET = 'DB_UNIX_SOCKET';
+ case DB_PORT = 'DB_PORT';
+ case GEOLITE_LICENSE_KEY = 'GEOLITE_LICENSE_KEY';
+ case REDIS_SERVERS = 'REDIS_SERVERS';
+ case REDIS_SENTINEL_SERVICE = 'REDIS_SENTINEL_SERVICE';
+ case MERCURE_PUBLIC_HUB_URL = 'MERCURE_PUBLIC_HUB_URL';
+ case MERCURE_INTERNAL_HUB_URL = 'MERCURE_INTERNAL_HUB_URL';
+ case MERCURE_JWT_SECRET = 'MERCURE_JWT_SECRET';
+ case DEFAULT_QR_CODE_SIZE = 'DEFAULT_QR_CODE_SIZE';
+ case DEFAULT_QR_CODE_MARGIN = 'DEFAULT_QR_CODE_MARGIN';
+ case DEFAULT_QR_CODE_FORMAT = 'DEFAULT_QR_CODE_FORMAT';
+ case DEFAULT_QR_CODE_ERROR_CORRECTION = 'DEFAULT_QR_CODE_ERROR_CORRECTION';
+ case DEFAULT_QR_CODE_ROUND_BLOCK_SIZE = 'DEFAULT_QR_CODE_ROUND_BLOCK_SIZE';
+ case RABBITMQ_ENABLED = 'RABBITMQ_ENABLED';
+ case RABBITMQ_HOST = 'RABBITMQ_HOST';
+ case RABBITMQ_PORT = 'RABBITMQ_PORT';
+ case RABBITMQ_USER = 'RABBITMQ_USER';
+ case RABBITMQ_PASSWORD = 'RABBITMQ_PASSWORD';
+ case RABBITMQ_VHOST = 'RABBITMQ_VHOST';
+ case DEFAULT_INVALID_SHORT_URL_REDIRECT = 'DEFAULT_INVALID_SHORT_URL_REDIRECT';
+ case DEFAULT_REGULAR_404_REDIRECT = 'DEFAULT_REGULAR_404_REDIRECT';
+ case DEFAULT_BASE_URL_REDIRECT = 'DEFAULT_BASE_URL_REDIRECT';
+ case REDIRECT_STATUS_CODE = 'REDIRECT_STATUS_CODE';
+ case REDIRECT_CACHE_LIFETIME = 'REDIRECT_CACHE_LIFETIME';
+ case BASE_PATH = 'BASE_PATH';
+ case PORT = 'PORT';
+ case TASK_WORKER_NUM = 'TASK_WORKER_NUM';
+ case WEB_WORKER_NUM = 'WEB_WORKER_NUM';
+ case ANONYMIZE_REMOTE_ADDR = 'ANONYMIZE_REMOTE_ADDR';
+ case TRACK_ORPHAN_VISITS = 'TRACK_ORPHAN_VISITS';
+ case DISABLE_TRACK_PARAM = 'DISABLE_TRACK_PARAM';
+ case DISABLE_TRACKING = 'DISABLE_TRACKING';
+ case DISABLE_IP_TRACKING = 'DISABLE_IP_TRACKING';
+ case DISABLE_REFERRER_TRACKING = 'DISABLE_REFERRER_TRACKING';
+ case DISABLE_UA_TRACKING = 'DISABLE_UA_TRACKING';
+ case DISABLE_TRACKING_FROM = 'DISABLE_TRACKING_FROM';
+ case DEFAULT_SHORT_CODES_LENGTH = 'DEFAULT_SHORT_CODES_LENGTH';
+ case IS_HTTPS_ENABLED = 'IS_HTTPS_ENABLED';
+ case DEFAULT_DOMAIN = 'DEFAULT_DOMAIN';
+ case AUTO_RESOLVE_TITLES = 'AUTO_RESOLVE_TITLES';
+ case REDIRECT_APPEND_EXTRA_PATH = 'REDIRECT_APPEND_EXTRA_PATH';
+ case TIMEZONE = 'TIMEZONE';
/** @deprecated */
- public const VISITS_WEBHOOKS = 'VISITS_WEBHOOKS';
+ case VISITS_WEBHOOKS = 'VISITS_WEBHOOKS';
/** @deprecated */
- public const NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS = 'NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS';
-
- /**
- * @return string[]
- */
- public static function cases(): array
- {
- static $constants;
- if ($constants !== null) {
- return $constants;
- }
-
- $ref = new ReflectionClass(self::class);
- return $constants = array_values($ref->getConstants(ReflectionClassConstant::IS_PUBLIC));
- }
-
- private function __construct(private string $envVar)
- {
- }
-
- public static function __callStatic(string $name, array $arguments): self
- {
- if (! contains(self::cases(), $name)) {
- throw new InvalidArgumentException('Invalid env var: "' . $name . '"');
- }
-
- return new self($name);
- }
+ case NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS = 'NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS';
public function loadFromEnv(mixed $default = null): mixed
{
- return env($this->envVar, $default);
+ return env($this->value, $default);
}
public function existsInEnv(): bool
diff --git a/module/Core/src/Config/NotFoundRedirectResolver.php b/module/Core/src/Config/NotFoundRedirectResolver.php
index caa100c3..3ab2e740 100644
--- a/module/Core/src/Config/NotFoundRedirectResolver.php
+++ b/module/Core/src/Config/NotFoundRedirectResolver.php
@@ -13,7 +13,9 @@ use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType;
use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface;
use function Functional\compose;
+use function Functional\id;
use function str_replace;
+use function urlencode;
class NotFoundRedirectResolver implements NotFoundRedirectResolverInterface
{
@@ -71,10 +73,10 @@ class NotFoundRedirectResolver implements NotFoundRedirectResolverInterface
$replacePlaceholderForPattern(self::ORIGINAL_PATH_PLACEHOLDER, $path, $modifier),
);
$replacePlaceholdersInPath = compose(
- $replacePlaceholders('\Functional\id'),
- static fn (?string $path) => $path === null ? null : str_replace('//', '/', $path), // Fix duplicated bars
+ $replacePlaceholders(id(...)),
+ static fn (?string $path) => $path === null ? null : str_replace('//', '/', $path),
);
- $replacePlaceholdersInQuery = $replacePlaceholders('\urlencode');
+ $replacePlaceholdersInQuery = $replacePlaceholders(urlencode(...));
return $redirectUri
->withPath($replacePlaceholdersInPath($redirectUri->getPath()))
diff --git a/module/Core/src/Config/NotFoundRedirects.php b/module/Core/src/Config/NotFoundRedirects.php
index 492a00bc..48437924 100644
--- a/module/Core/src/Config/NotFoundRedirects.php
+++ b/module/Core/src/Config/NotFoundRedirects.php
@@ -9,9 +9,9 @@ use JsonSerializable;
final class NotFoundRedirects implements JsonSerializable
{
private function __construct(
- private ?string $baseUrlRedirect,
- private ?string $regular404Redirect,
- private ?string $invalidShortUrlRedirect,
+ public readonly ?string $baseUrlRedirect,
+ public readonly ?string $regular404Redirect,
+ public readonly ?string $invalidShortUrlRedirect,
) {
}
@@ -33,21 +33,6 @@ final class NotFoundRedirects implements JsonSerializable
return new self($config->baseUrlRedirect(), $config->regular404Redirect(), $config->invalidShortUrlRedirect());
}
- public function baseUrlRedirect(): ?string
- {
- return $this->baseUrlRedirect;
- }
-
- public function regular404Redirect(): ?string
- {
- return $this->regular404Redirect;
- }
-
- public function invalidShortUrlRedirect(): ?string
- {
- return $this->invalidShortUrlRedirect;
- }
-
public function jsonSerialize(): array
{
return [
diff --git a/module/Core/src/Domain/Model/DomainItem.php b/module/Core/src/Domain/Model/DomainItem.php
index 5547fe8d..cc968e95 100644
--- a/module/Core/src/Domain/Model/DomainItem.php
+++ b/module/Core/src/Domain/Model/DomainItem.php
@@ -12,9 +12,9 @@ use Shlinkio\Shlink\Core\Entity\Domain;
final class DomainItem implements JsonSerializable
{
private function __construct(
- private string $authority,
- private NotFoundRedirectConfigInterface $notFoundRedirectConfig,
- private bool $isDefault,
+ private readonly string $authority,
+ public readonly NotFoundRedirectConfigInterface $notFoundRedirectConfig,
+ public readonly bool $isDefault,
) {
}
@@ -23,9 +23,9 @@ final class DomainItem implements JsonSerializable
return new self($domain->getAuthority(), $domain, false);
}
- public static function forDefaultDomain(string $authority, NotFoundRedirectConfigInterface $config): self
+ public static function forDefaultDomain(string $defaultDomain, NotFoundRedirectConfigInterface $config): self
{
- return new self($authority, $config, true);
+ return new self($defaultDomain, $config, true);
}
public function jsonSerialize(): array
@@ -41,14 +41,4 @@ final class DomainItem implements JsonSerializable
{
return $this->authority;
}
-
- public function isDefault(): bool
- {
- return $this->isDefault;
- }
-
- public function notFoundRedirectConfig(): NotFoundRedirectConfigInterface
- {
- return $this->notFoundRedirectConfig;
- }
}
diff --git a/module/Core/src/Domain/Repository/DomainRepository.php b/module/Core/src/Domain/Repository/DomainRepository.php
index 0a99b3c6..60c32499 100644
--- a/module/Core/src/Domain/Repository/DomainRepository.php
+++ b/module/Core/src/Domain/Repository/DomainRepository.php
@@ -7,7 +7,6 @@ namespace Shlinkio\Shlink\Core\Domain\Repository;
use Doctrine\ORM\Query\Expr\Join;
use Doctrine\ORM\QueryBuilder;
use Happyr\DoctrineSpecification\Repository\EntitySpecificationRepository;
-use Happyr\DoctrineSpecification\Spec;
use Shlinkio\Shlink\Core\Domain\Spec\IsDomain;
use Shlinkio\Shlink\Core\Entity\Domain;
use Shlinkio\Shlink\Core\Entity\ShortUrl;
@@ -77,10 +76,9 @@ class DomainRepository extends EntitySpecificationRepository implements DomainRe
// FIXME The $apiKey->spec() method cannot be used here, as it returns a single spec which assumes the
// ShortUrl is the root entity. Here, the Domain is the root entity.
// Think on a way to centralize the conditional behavior and make $apiKey->spec() more flexible.
- yield from $apiKey?->mapRoles(fn (string $roleName, array $meta) => match ($roleName) {
+ yield from $apiKey?->mapRoles(fn (Role $role, array $meta) => match ($role) {
Role::DOMAIN_SPECIFIC => ['d', new IsDomain(Role::domainIdFromMeta($meta))],
Role::AUTHORED_SHORT_URLS => ['s', new BelongsToApiKey($apiKey)],
- default => [null, Spec::andX()],
}) ?? [];
}
}
diff --git a/module/Core/src/Entity/Domain.php b/module/Core/src/Entity/Domain.php
index 65ca8ce6..9c31bbe2 100644
--- a/module/Core/src/Entity/Domain.php
+++ b/module/Core/src/Entity/Domain.php
@@ -66,8 +66,8 @@ class Domain extends AbstractEntity implements JsonSerializable, NotFoundRedirec
public function configureNotFoundRedirects(NotFoundRedirects $redirects): void
{
- $this->baseUrlRedirect = $redirects->baseUrlRedirect();
- $this->regular404Redirect = $redirects->regular404Redirect();
- $this->invalidShortUrlRedirect = $redirects->invalidShortUrlRedirect();
+ $this->baseUrlRedirect = $redirects->baseUrlRedirect;
+ $this->regular404Redirect = $redirects->regular404Redirect;
+ $this->invalidShortUrlRedirect = $redirects->invalidShortUrlRedirect;
}
}
diff --git a/module/Core/src/Entity/ShortUrl.php b/module/Core/src/Entity/ShortUrl.php
index 9fff1509..28c4c446 100644
--- a/module/Core/src/Entity/ShortUrl.php
+++ b/module/Core/src/Entity/ShortUrl.php
@@ -16,6 +16,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\ShortUrl\Resolver\ShortUrlRelationResolverInterface;
use Shlinkio\Shlink\Core\ShortUrl\Resolver\SimpleShortUrlRelationResolver;
use Shlinkio\Shlink\Core\Validation\ShortUrlInputFilter;
+use Shlinkio\Shlink\Core\Visit\Model\VisitType;
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
@@ -174,7 +175,7 @@ class ShortUrl extends AbstractEntity
{
/** @var Selectable $visits */
$visits = $this->visits;
- $criteria = Criteria::create()->where(Criteria::expr()->eq('type', Visit::TYPE_IMPORTED))
+ $criteria = Criteria::create()->where(Criteria::expr()->eq('type', VisitType::IMPORTED))
->orderBy(['id' => 'DESC'])
->setMaxResults(1);
diff --git a/module/Core/src/Entity/Visit.php b/module/Core/src/Entity/Visit.php
index c509bcc3..23a518ca 100644
--- a/module/Core/src/Entity/Visit.php
+++ b/module/Core/src/Entity/Visit.php
@@ -11,29 +11,24 @@ use Shlinkio\Shlink\Common\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Common\Util\IpAddress;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Visit\Model\VisitLocationInterface;
+use Shlinkio\Shlink\Core\Visit\Model\VisitType;
use Shlinkio\Shlink\Importer\Model\ImportedShlinkVisit;
use function Shlinkio\Shlink\Core\isCrawler;
class Visit extends AbstractEntity implements JsonSerializable
{
- public const TYPE_VALID_SHORT_URL = 'valid_short_url';
- public const TYPE_IMPORTED = 'imported';
- public const TYPE_INVALID_SHORT_URL = 'invalid_short_url';
- public const TYPE_BASE_URL = 'base_url';
- public const TYPE_REGULAR_404 = 'regular_404';
-
private string $referer;
private Chronos $date;
private ?string $remoteAddr = null;
private ?string $visitedUrl = null;
private string $userAgent;
- private string $type;
+ private VisitType $type;
private ?ShortUrl $shortUrl;
private ?VisitLocation $visitLocation = null;
private bool $potentialBot;
- private function __construct(?ShortUrl $shortUrl, string $type)
+ private function __construct(?ShortUrl $shortUrl, VisitType $type)
{
$this->shortUrl = $shortUrl;
$this->date = Chronos::now();
@@ -42,7 +37,7 @@ class Visit extends AbstractEntity implements JsonSerializable
public static function forValidShortUrl(ShortUrl $shortUrl, Visitor $visitor, bool $anonymize = true): self
{
- $instance = new self($shortUrl, self::TYPE_VALID_SHORT_URL);
+ $instance = new self($shortUrl, VisitType::VALID_SHORT_URL);
$instance->hydrateFromVisitor($visitor, $anonymize);
return $instance;
@@ -50,7 +45,7 @@ class Visit extends AbstractEntity implements JsonSerializable
public static function fromImport(ShortUrl $shortUrl, ImportedShlinkVisit $importedVisit): self
{
- $instance = new self($shortUrl, self::TYPE_IMPORTED);
+ $instance = new self($shortUrl, VisitType::IMPORTED);
$instance->userAgent = $importedVisit->userAgent();
$instance->potentialBot = isCrawler($instance->userAgent);
$instance->referer = $importedVisit->referer();
@@ -64,7 +59,7 @@ class Visit extends AbstractEntity implements JsonSerializable
public static function forBasePath(Visitor $visitor, bool $anonymize = true): self
{
- $instance = new self(null, self::TYPE_BASE_URL);
+ $instance = new self(null, VisitType::BASE_URL);
$instance->hydrateFromVisitor($visitor, $anonymize);
return $instance;
@@ -72,7 +67,7 @@ class Visit extends AbstractEntity implements JsonSerializable
public static function forInvalidShortUrl(Visitor $visitor, bool $anonymize = true): self
{
- $instance = new self(null, self::TYPE_INVALID_SHORT_URL);
+ $instance = new self(null, VisitType::INVALID_SHORT_URL);
$instance->hydrateFromVisitor($visitor, $anonymize);
return $instance;
@@ -80,7 +75,7 @@ class Visit extends AbstractEntity implements JsonSerializable
public static function forRegularNotFound(Visitor $visitor, bool $anonymize = true): self
{
- $instance = new self(null, self::TYPE_REGULAR_404);
+ $instance = new self(null, VisitType::REGULAR_404);
$instance->hydrateFromVisitor($visitor, $anonymize);
return $instance;
@@ -88,10 +83,10 @@ class Visit extends AbstractEntity implements JsonSerializable
private function hydrateFromVisitor(Visitor $visitor, bool $anonymize = true): void
{
- $this->userAgent = $visitor->getUserAgent();
- $this->referer = $visitor->getReferer();
- $this->remoteAddr = $this->processAddress($anonymize, $visitor->getRemoteAddress());
- $this->visitedUrl = $visitor->getVisitedUrl();
+ $this->userAgent = $visitor->userAgent;
+ $this->referer = $visitor->referer;
+ $this->remoteAddr = $this->processAddress($anonymize, $visitor->remoteAddress);
+ $this->visitedUrl = $visitor->visitedUrl;
$this->potentialBot = $visitor->isPotentialBot();
}
@@ -150,7 +145,7 @@ class Visit extends AbstractEntity implements JsonSerializable
return $this->visitedUrl;
}
- public function type(): string
+ public function type(): VisitType
{
return $this->type;
}
@@ -159,11 +154,19 @@ class Visit extends AbstractEntity implements JsonSerializable
* Needed only for ArrayCollections to be able to apply criteria filtering
* @internal
*/
- public function getType(): string
+ public function getType(): VisitType
{
return $this->type();
}
+ /**
+ * @internal
+ */
+ public function getDate(): Chronos
+ {
+ return $this->date;
+ }
+
public function jsonSerialize(): array
{
return [
@@ -174,12 +177,4 @@ class Visit extends AbstractEntity implements JsonSerializable
'potentialBot' => $this->potentialBot,
];
}
-
- /**
- * @internal
- */
- public function getDate(): Chronos
- {
- return $this->date;
- }
}
diff --git a/module/Core/src/ErrorHandler/Model/NotFoundType.php b/module/Core/src/ErrorHandler/Model/NotFoundType.php
index 39970dea..f95368cb 100644
--- a/module/Core/src/ErrorHandler/Model/NotFoundType.php
+++ b/module/Core/src/ErrorHandler/Model/NotFoundType.php
@@ -7,13 +7,13 @@ namespace Shlinkio\Shlink\Core\ErrorHandler\Model;
use Mezzio\Router\RouteResult;
use Psr\Http\Message\ServerRequestInterface;
use Shlinkio\Shlink\Core\Action\RedirectAction;
-use Shlinkio\Shlink\Core\Entity\Visit;
+use Shlinkio\Shlink\Core\Visit\Model\VisitType;
use function rtrim;
class NotFoundType
{
- private function __construct(private string $type)
+ private function __construct(private readonly VisitType $type)
{
}
@@ -24,10 +24,10 @@ class NotFoundType
$isBaseUrl = rtrim($request->getUri()->getPath(), '/') === $basePath;
$type = match (true) {
- $isBaseUrl => Visit::TYPE_BASE_URL,
- $routeResult->isFailure() => Visit::TYPE_REGULAR_404,
- $routeResult->getMatchedRouteName() === RedirectAction::class => Visit::TYPE_INVALID_SHORT_URL,
- default => self::class,
+ $isBaseUrl => VisitType::BASE_URL,
+ $routeResult->isFailure() => VisitType::REGULAR_404,
+ $routeResult->getMatchedRouteName() === RedirectAction::class => VisitType::INVALID_SHORT_URL,
+ default => VisitType::VALID_SHORT_URL,
};
return new self($type);
@@ -35,16 +35,16 @@ class NotFoundType
public function isBaseUrl(): bool
{
- return $this->type === Visit::TYPE_BASE_URL;
+ return $this->type === VisitType::BASE_URL;
}
public function isRegularNotFound(): bool
{
- return $this->type === Visit::TYPE_REGULAR_404;
+ return $this->type === VisitType::REGULAR_404;
}
public function isInvalidShortUrl(): bool
{
- return $this->type === Visit::TYPE_INVALID_SHORT_URL;
+ return $this->type === VisitType::INVALID_SHORT_URL;
}
}
diff --git a/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php b/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php
index c4bc1818..6fadaa5d 100644
--- a/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php
+++ b/module/Core/src/EventDispatcher/Event/AbstractVisitEvent.php
@@ -8,15 +8,10 @@ use JsonSerializable;
abstract class AbstractVisitEvent implements JsonSerializable
{
- public function __construct(protected string $visitId)
+ public function __construct(public readonly string $visitId)
{
}
- public function visitId(): string
- {
- return $this->visitId;
- }
-
public function jsonSerialize(): array
{
return ['visitId' => $this->visitId];
diff --git a/module/Core/src/EventDispatcher/Event/UrlVisited.php b/module/Core/src/EventDispatcher/Event/UrlVisited.php
index 633b439e..02452a3e 100644
--- a/module/Core/src/EventDispatcher/Event/UrlVisited.php
+++ b/module/Core/src/EventDispatcher/Event/UrlVisited.php
@@ -6,13 +6,8 @@ namespace Shlinkio\Shlink\Core\EventDispatcher\Event;
final class UrlVisited extends AbstractVisitEvent
{
- public function __construct(string $visitId, private ?string $originalIpAddress = null)
+ public function __construct(string $visitId, public readonly ?string $originalIpAddress = null)
{
parent::__construct($visitId);
}
-
- public function originalIpAddress(): ?string
- {
- return $this->originalIpAddress;
- }
}
diff --git a/module/Core/src/EventDispatcher/LocateVisit.php b/module/Core/src/EventDispatcher/LocateVisit.php
index fbd32962..197ce9a0 100644
--- a/module/Core/src/EventDispatcher/LocateVisit.php
+++ b/module/Core/src/EventDispatcher/LocateVisit.php
@@ -30,7 +30,7 @@ class LocateVisit
public function __invoke(UrlVisited $shortUrlVisited): void
{
- $visitId = $shortUrlVisited->visitId();
+ $visitId = $shortUrlVisited->visitId;
/** @var Visit|null $visit */
$visit = $this->em->find(Visit::class, $visitId);
@@ -41,7 +41,7 @@ class LocateVisit
return;
}
- $this->locateVisit($visitId, $shortUrlVisited->originalIpAddress(), $visit);
+ $this->locateVisit($visitId, $shortUrlVisited->originalIpAddress, $visit);
$this->eventDispatcher->dispatch(new VisitLocated($visitId));
}
diff --git a/module/Core/src/EventDispatcher/NotifyVisitToMercure.php b/module/Core/src/EventDispatcher/NotifyVisitToMercure.php
index ed205f40..096e7e65 100644
--- a/module/Core/src/EventDispatcher/NotifyVisitToMercure.php
+++ b/module/Core/src/EventDispatcher/NotifyVisitToMercure.php
@@ -27,7 +27,7 @@ class NotifyVisitToMercure
public function __invoke(VisitLocated $shortUrlLocated): void
{
- $visitId = $shortUrlLocated->visitId();
+ $visitId = $shortUrlLocated->visitId;
/** @var Visit|null $visit */
$visit = $this->em->find(Visit::class, $visitId);
diff --git a/module/Core/src/EventDispatcher/NotifyVisitToRabbitMq.php b/module/Core/src/EventDispatcher/NotifyVisitToRabbitMq.php
index f05ecf64..a81f2cab 100644
--- a/module/Core/src/EventDispatcher/NotifyVisitToRabbitMq.php
+++ b/module/Core/src/EventDispatcher/NotifyVisitToRabbitMq.php
@@ -37,7 +37,7 @@ class NotifyVisitToRabbitMq
return;
}
- $visitId = $shortUrlLocated->visitId();
+ $visitId = $shortUrlLocated->visitId;
$visit = $this->em->find(Visit::class, $visitId);
if ($visit === null) {
diff --git a/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php b/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php
index 73ff9266..1bf09517 100644
--- a/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php
+++ b/module/Core/src/EventDispatcher/NotifyVisitToWebHooks.php
@@ -40,7 +40,7 @@ class NotifyVisitToWebHooks
return;
}
- $visitId = $shortUrlLocated->visitId();
+ $visitId = $shortUrlLocated->visitId;
/** @var Visit|null $visit */
$visit = $this->em->find(Visit::class, $visitId);
diff --git a/module/Core/src/Exception/DeleteShortUrlException.php b/module/Core/src/Exception/DeleteShortUrlException.php
index e6f3bd0d..0d331400 100644
--- a/module/Core/src/Exception/DeleteShortUrlException.php
+++ b/module/Core/src/Exception/DeleteShortUrlException.php
@@ -20,8 +20,8 @@ class DeleteShortUrlException extends DomainException implements ProblemDetailsE
public static function fromVisitsThreshold(int $threshold, ShortUrlIdentifier $identifier): self
{
- $shortCode = $identifier->shortCode();
- $domain = $identifier->domain();
+ $shortCode = $identifier->shortCode;
+ $domain = $identifier->domain;
$suffix = $domain === null ? '' : sprintf(' for domain "%s"', $domain);
$e = new self(sprintf(
'Impossible to delete short URL with short code "%s"%s, since it has more than "%s" visits.',
diff --git a/module/Core/src/Exception/ShortUrlNotFoundException.php b/module/Core/src/Exception/ShortUrlNotFoundException.php
index 0ae29da5..c59c20ef 100644
--- a/module/Core/src/Exception/ShortUrlNotFoundException.php
+++ b/module/Core/src/Exception/ShortUrlNotFoundException.php
@@ -20,8 +20,8 @@ class ShortUrlNotFoundException extends DomainException implements ProblemDetail
public static function fromNotFound(ShortUrlIdentifier $identifier): self
{
- $shortCode = $identifier->shortCode();
- $domain = $identifier->domain();
+ $shortCode = $identifier->shortCode;
+ $domain = $identifier->domain;
$suffix = $domain === null ? '' : sprintf(' for domain "%s"', $domain);
$e = new self(sprintf('No URL found with short code "%s"%s', $shortCode, $suffix));
diff --git a/module/Core/src/Importer/ShortUrlImporting.php b/module/Core/src/Importer/ShortUrlImporting.php
index e1680517..4aa87166 100644
--- a/module/Core/src/Importer/ShortUrlImporting.php
+++ b/module/Core/src/Importer/ShortUrlImporting.php
@@ -14,7 +14,7 @@ use function sprintf;
final class ShortUrlImporting
{
- private function __construct(private ShortUrl $shortUrl, private bool $isNew)
+ private function __construct(private readonly ShortUrl $shortUrl, private readonly bool $isNew)
{
}
diff --git a/module/Core/src/Model/AbstractInfinitePaginableListParams.php b/module/Core/src/Model/AbstractInfinitePaginableListParams.php
index ae107fdc..d4b2aaab 100644
--- a/module/Core/src/Model/AbstractInfinitePaginableListParams.php
+++ b/module/Core/src/Model/AbstractInfinitePaginableListParams.php
@@ -10,8 +10,8 @@ abstract class AbstractInfinitePaginableListParams
{
private const FIRST_PAGE = 1;
- private int $page;
- private int $itemsPerPage;
+ public readonly int $page;
+ public readonly int $itemsPerPage;
protected function __construct(?int $page, ?int $itemsPerPage)
{
@@ -28,14 +28,4 @@ abstract class AbstractInfinitePaginableListParams
{
return $itemsPerPage === null || $itemsPerPage < 0 ? Paginator::ALL_ITEMS : $itemsPerPage;
}
-
- public function getPage(): int
- {
- return $this->page;
- }
-
- public function getItemsPerPage(): int
- {
- return $this->itemsPerPage;
- }
}
diff --git a/module/Core/src/Model/Ordering.php b/module/Core/src/Model/Ordering.php
index bd648227..5adbb161 100644
--- a/module/Core/src/Model/Ordering.php
+++ b/module/Core/src/Model/Ordering.php
@@ -8,7 +8,7 @@ final class Ordering
{
private const DEFAULT_DIR = 'ASC';
- private function __construct(private ?string $field, private string $dir)
+ private function __construct(public readonly ?string $field, public readonly string $direction)
{
}
@@ -26,16 +26,6 @@ final class Ordering
return self::fromTuple([null, null]);
}
- public function orderField(): ?string
- {
- return $this->field;
- }
-
- public function orderDirection(): string
- {
- return $this->dir;
- }
-
public function hasOrderField(): bool
{
return $this->field !== null;
diff --git a/module/Core/src/Model/ShortUrlIdentifier.php b/module/Core/src/Model/ShortUrlIdentifier.php
index 815a5313..d2d6cbbc 100644
--- a/module/Core/src/Model/ShortUrlIdentifier.php
+++ b/module/Core/src/Model/ShortUrlIdentifier.php
@@ -10,7 +10,7 @@ use Symfony\Component\Console\Input\InputInterface;
final class ShortUrlIdentifier
{
- public function __construct(private string $shortCode, private ?string $domain = null)
+ private function __construct(public readonly string $shortCode, public readonly ?string $domain = null)
{
}
@@ -54,14 +54,4 @@ final class ShortUrlIdentifier
{
return new self($shortCode, $domain);
}
-
- public function shortCode(): string
- {
- return $this->shortCode;
- }
-
- public function domain(): ?string
- {
- return $this->domain;
- }
}
diff --git a/module/Core/src/Model/ShortUrlsParams.php b/module/Core/src/Model/ShortUrlsParams.php
index 95cf4df6..bd6dc556 100644
--- a/module/Core/src/Model/ShortUrlsParams.php
+++ b/module/Core/src/Model/ShortUrlsParams.php
@@ -6,6 +6,7 @@ namespace Shlinkio\Shlink\Core\Model;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Exception\ValidationException;
+use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
use Shlinkio\Shlink\Core\Validation\ShortUrlsParamsInputFilter;
use function Shlinkio\Shlink\Common\buildDateRange;
@@ -15,15 +16,12 @@ final class ShortUrlsParams
{
public const ORDERABLE_FIELDS = ['longUrl', 'shortCode', 'dateCreated', 'title', 'visits'];
public const DEFAULT_ITEMS_PER_PAGE = 10;
- public const TAGS_MODE_ANY = 'any';
- public const TAGS_MODE_ALL = 'all';
private int $page;
private int $itemsPerPage;
private ?string $searchTerm;
private array $tags;
- /** @var self::TAGS_MODE_ANY|self::TAGS_MODE_ALL */
- private string $tagsMode = self::TAGS_MODE_ANY;
+ private TagsMode $tagsMode = TagsMode::ANY;
private Ordering $orderBy;
private ?DateRange $dateRange;
@@ -68,7 +66,16 @@ final class ShortUrlsParams
$this->itemsPerPage = (int) (
$inputFilter->getValue(ShortUrlsParamsInputFilter::ITEMS_PER_PAGE) ?? self::DEFAULT_ITEMS_PER_PAGE
);
- $this->tagsMode = $inputFilter->getValue(ShortUrlsParamsInputFilter::TAGS_MODE) ?? self::TAGS_MODE_ANY;
+ $this->tagsMode = $this->resolveTagsMode($inputFilter->getValue(ShortUrlsParamsInputFilter::TAGS_MODE));
+ }
+
+ private function resolveTagsMode(?string $rawTagsMode): TagsMode
+ {
+ if ($rawTagsMode === null) {
+ return TagsMode::ANY;
+ }
+
+ return TagsMode::tryFrom($rawTagsMode) ?? TagsMode::ANY;
}
public function page(): int
@@ -101,10 +108,7 @@ final class ShortUrlsParams
return $this->dateRange;
}
- /**
- * @return self::TAGS_MODE_ANY|self::TAGS_MODE_ALL
- */
- public function tagsMode(): string
+ public function tagsMode(): TagsMode
{
return $this->tagsMode;
}
diff --git a/module/Core/src/Model/Visitor.php b/module/Core/src/Model/Visitor.php
index 9436e900..2207fad8 100644
--- a/module/Core/src/Model/Visitor.php
+++ b/module/Core/src/Model/Visitor.php
@@ -18,10 +18,10 @@ final class Visitor
public const REMOTE_ADDRESS_MAX_LENGTH = 256;
public const VISITED_URL_MAX_LENGTH = 2048;
- private string $userAgent;
- private string $referer;
- private string $visitedUrl;
- private ?string $remoteAddress;
+ public readonly string $userAgent;
+ public readonly string $referer;
+ public readonly string $visitedUrl;
+ public readonly ?string $remoteAddress;
private bool $potentialBot;
public function __construct(string $userAgent, string $referer, ?string $remoteAddress, string $visitedUrl)
@@ -61,26 +61,6 @@ final class Visitor
return new self('cf-facebook', '', null, '');
}
- public function getUserAgent(): string
- {
- return $this->userAgent;
- }
-
- public function getReferer(): string
- {
- return $this->referer;
- }
-
- public function getRemoteAddress(): ?string
- {
- return $this->remoteAddress;
- }
-
- public function getVisitedUrl(): string
- {
- return $this->visitedUrl;
- }
-
public function isPotentialBot(): bool
{
return $this->potentialBot;
diff --git a/module/Core/src/Model/VisitsParams.php b/module/Core/src/Model/VisitsParams.php
index 718a4bc5..ab9e8002 100644
--- a/module/Core/src/Model/VisitsParams.php
+++ b/module/Core/src/Model/VisitsParams.php
@@ -10,13 +10,13 @@ use function Shlinkio\Shlink\Core\parseDateRangeFromQuery;
final class VisitsParams extends AbstractInfinitePaginableListParams
{
- private DateRange $dateRange;
+ public readonly DateRange $dateRange;
public function __construct(
?DateRange $dateRange = null,
?int $page = null,
?int $itemsPerPage = null,
- private bool $excludeBots = false,
+ public readonly bool $excludeBots = false,
) {
parent::__construct($page, $itemsPerPage);
$this->dateRange = $dateRange ?? DateRange::emptyInstance();
@@ -31,14 +31,4 @@ final class VisitsParams extends AbstractInfinitePaginableListParams
isset($query['excludeBots']),
);
}
-
- public function getDateRange(): DateRange
- {
- return $this->dateRange;
- }
-
- public function excludeBots(): bool
- {
- return $this->excludeBots;
- }
}
diff --git a/module/Core/src/Repository/ShortUrlRepository.php b/module/Core/src/Repository/ShortUrlRepository.php
index 9852c530..7a3def16 100644
--- a/module/Core/src/Repository/ShortUrlRepository.php
+++ b/module/Core/src/Repository/ShortUrlRepository.php
@@ -15,7 +15,7 @@ use Shlinkio\Shlink\Core\Entity\ShortUrl;
use Shlinkio\Shlink\Core\Model\Ordering;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
-use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
+use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsCountFiltering;
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsListFiltering;
use Shlinkio\Shlink\Importer\Model\ImportedShlinkUrl;
@@ -47,8 +47,8 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
private function processOrderByForList(QueryBuilder $qb, Ordering $orderBy): array
{
- $fieldName = $orderBy->orderField();
- $order = $orderBy->orderDirection();
+ $fieldName = $orderBy->field;
+ $order = $orderBy->direction;
if ($fieldName === 'visits') {
// FIXME This query is inefficient.
@@ -116,8 +116,8 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
// Filter by tags if provided
if (! empty($tags)) {
- $tagsMode = $filtering->tagsMode() ?? ShortUrlsParams::TAGS_MODE_ANY;
- $tagsMode === ShortUrlsParams::TAGS_MODE_ANY
+ $tagsMode = $filtering->tagsMode() ?? TagsMode::ANY;
+ $tagsMode === TagsMode::ANY
? $qb->join('s.tags', 't')->andWhere($qb->expr()->in('t.name', $tags))
: $this->joinAllTags($qb, $tags);
}
@@ -146,8 +146,8 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
$query = $this->getEntityManager()->createQuery($dql);
$query->setMaxResults(1)
->setParameters([
- 'shortCode' => $identifier->shortCode(),
- 'domain' => $identifier->domain(),
+ 'shortCode' => $identifier->shortCode,
+ 'domain' => $identifier->domain,
]);
// Since we ordered by domain, we will have first the URL matching provided domain, followed by the one
@@ -198,10 +198,10 @@ class ShortUrlRepository extends EntitySpecificationRepository implements ShortU
$qb->from(ShortUrl::class, 's')
->where($qb->expr()->isNotNull('s.shortCode'))
->andWhere($qb->expr()->eq('s.shortCode', ':slug'))
- ->setParameter('slug', $identifier->shortCode())
+ ->setParameter('slug', $identifier->shortCode)
->setMaxResults(1);
- $this->whereDomainIs($qb, $identifier->domain());
+ $this->whereDomainIs($qb, $identifier->domain);
$this->applySpecification($qb, $spec, 's');
diff --git a/module/Core/src/Repository/TagRepository.php b/module/Core/src/Repository/TagRepository.php
index aa24e0a1..2a144b38 100644
--- a/module/Core/src/Repository/TagRepository.php
+++ b/module/Core/src/Repository/TagRepository.php
@@ -41,8 +41,8 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
*/
public function findTagsWithInfo(?TagsListFiltering $filtering = null): array
{
- $orderField = $filtering?->orderBy()?->orderField();
- $orderDir = $filtering?->orderBy()?->orderDirection();
+ $orderField = $filtering?->orderBy?->field;
+ $orderDir = $filtering?->orderBy?->direction;
$orderMainQuery = contains(['shortUrlsCount', 'visitsCount'], $orderField);
$conn = $this->getEntityManager()->getConnection();
@@ -51,16 +51,16 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
if (! $orderMainQuery) {
$subQb->orderBy('t.name', $orderDir ?? 'ASC')
- ->setMaxResults($filtering?->limit() ?? PHP_INT_MAX)
- ->setFirstResult($filtering?->offset() ?? 0);
+ ->setMaxResults($filtering?->limit ?? PHP_INT_MAX)
+ ->setFirstResult($filtering?->offset ?? 0);
}
- $searchTerm = $filtering?->searchTerm();
+ $searchTerm = $filtering?->searchTerm;
if ($searchTerm !== null) {
$subQb->andWhere($subQb->expr()->like('t.name', $conn->quote('%' . $searchTerm . '%')));
}
- $apiKey = $filtering?->apiKey();
+ $apiKey = $filtering?->apiKey;
$this->applySpecification($subQb, new WithInlinedApiKeySpecsEnsuringJoin($apiKey), 't');
// A native query builder needs to be used here, because DQL and ORM query builders do not support
@@ -81,14 +81,13 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
->groupBy('t.id_0', 't.name_1');
// Apply API key role conditions to the native query too, as they will affect the amounts on the aggregates
- $apiKey?->mapRoles(static fn (string $roleName, array $meta) => match ($roleName) {
+ $apiKey?->mapRoles(static fn (Role $role, array $meta) => match ($role) {
Role::DOMAIN_SPECIFIC => $nativeQb->andWhere(
$nativeQb->expr()->eq('s.domain_id', $conn->quote(Role::domainIdFromMeta($meta))),
),
Role::AUTHORED_SHORT_URLS => $nativeQb->andWhere(
$nativeQb->expr()->eq('s.author_api_key_id', $conn->quote($apiKey->getId())),
),
- default => $nativeQb,
});
if ($orderMainQuery) {
@@ -97,8 +96,8 @@ class TagRepository extends EntitySpecificationRepository implements TagReposito
$orderField === 'shortUrlsCount' ? 'short_urls_count' : 'visits_count',
$orderDir ?? 'ASC',
)
- ->setMaxResults($filtering?->limit() ?? PHP_INT_MAX)
- ->setFirstResult($filtering?->offset() ?? 0);
+ ->setMaxResults($filtering?->limit ?? PHP_INT_MAX)
+ ->setFirstResult($filtering?->offset ?? 0);
}
// Add ordering by tag name, as a fallback in case of same amount, or as default ordering
diff --git a/module/Core/src/Repository/VisitRepository.php b/module/Core/src/Repository/VisitRepository.php
index 51a0c333..e0b51ce9 100644
--- a/module/Core/src/Repository/VisitRepository.php
+++ b/module/Core/src/Repository/VisitRepository.php
@@ -86,7 +86,7 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
public function findVisitsByShortCode(ShortUrlIdentifier $identifier, VisitsListFiltering $filtering): array
{
$qb = $this->createVisitsByShortCodeQueryBuilder($identifier, $filtering);
- return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit(), $filtering->offset());
+ return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit, $filtering->offset);
}
public function countVisitsByShortCode(ShortUrlIdentifier $identifier, VisitsCountFiltering $filtering): int
@@ -103,7 +103,7 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
): QueryBuilder {
/** @var ShortUrlRepositoryInterface $shortUrlRepo */
$shortUrlRepo = $this->getEntityManager()->getRepository(ShortUrl::class);
- $shortUrlId = $shortUrlRepo->findOne($identifier, $filtering->apiKey()?->spec())?->getId() ?? '-1';
+ $shortUrlId = $shortUrlRepo->findOne($identifier, $filtering->apiKey?->spec())?->getId() ?? '-1';
// Parameters in this query need to be part of the query itself, as we need to use it as sub-query later
// Since they are not provided by the caller, it's reasonably safe
@@ -111,12 +111,12 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
$qb->from(Visit::class, 'v')
->where($qb->expr()->eq('v.shortUrl', $shortUrlId));
- if ($filtering->excludeBots()) {
+ if ($filtering->excludeBots) {
$qb->andWhere($qb->expr()->eq('v.potentialBot', 'false'));
}
// Apply date range filtering
- $this->applyDatesInline($qb, $filtering->dateRange());
+ $this->applyDatesInline($qb, $filtering->dateRange);
return $qb;
}
@@ -124,7 +124,7 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
public function findVisitsByTag(string $tag, VisitsListFiltering $filtering): array
{
$qb = $this->createVisitsByTagQueryBuilder($tag, $filtering);
- return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit(), $filtering->offset());
+ return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit, $filtering->offset);
}
public function countVisitsByTag(string $tag, VisitsCountFiltering $filtering): int
@@ -144,12 +144,12 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
->join('s.tags', 't')
->where($qb->expr()->eq('t.name', $this->getEntityManager()->getConnection()->quote($tag)));
- if ($filtering->excludeBots()) {
+ if ($filtering->excludeBots) {
$qb->andWhere($qb->expr()->eq('v.potentialBot', 'false'));
}
- $this->applyDatesInline($qb, $filtering->dateRange());
- $this->applySpecification($qb, $filtering->apiKey()?->inlinedSpec(), 'v');
+ $this->applyDatesInline($qb, $filtering->dateRange);
+ $this->applySpecification($qb, $filtering->apiKey?->inlinedSpec(), 'v');
return $qb;
}
@@ -160,7 +160,7 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
public function findVisitsByDomain(string $domain, VisitsListFiltering $filtering): array
{
$qb = $this->createVisitsByDomainQueryBuilder($domain, $filtering);
- return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit(), $filtering->offset());
+ return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit, $filtering->offset);
}
public function countVisitsByDomain(string $domain, VisitsCountFiltering $filtering): int
@@ -185,12 +185,12 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
->where($qb->expr()->eq('d.authority', $this->getEntityManager()->getConnection()->quote($domain)));
}
- if ($filtering->excludeBots()) {
+ if ($filtering->excludeBots) {
$qb->andWhere($qb->expr()->eq('v.potentialBot', 'false'));
}
- $this->applyDatesInline($qb, $filtering->dateRange());
- $this->applySpecification($qb, $filtering->apiKey()?->inlinedSpec(), 'v');
+ $this->applyDatesInline($qb, $filtering->dateRange);
+ $this->applySpecification($qb, $filtering->apiKey?->inlinedSpec(), 'v');
return $qb;
}
@@ -199,7 +199,7 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
{
$qb = $this->createAllVisitsQueryBuilder($filtering);
$qb->andWhere($qb->expr()->isNull('v.shortUrl'));
- return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit(), $filtering->offset());
+ return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit, $filtering->offset);
}
public function countOrphanVisits(VisitsCountFiltering $filtering): int
@@ -215,9 +215,9 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
$qb = $this->createAllVisitsQueryBuilder($filtering);
$qb->andWhere($qb->expr()->isNotNull('v.shortUrl'));
- $this->applySpecification($qb, $filtering->apiKey()?->inlinedSpec());
+ $this->applySpecification($qb, $filtering->apiKey?->inlinedSpec());
- return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit(), $filtering->offset());
+ return $this->resolveVisitsWithNativeQuery($qb, $filtering->limit, $filtering->offset);
}
public function countNonOrphanVisits(VisitsCountFiltering $filtering): int
@@ -232,11 +232,11 @@ class VisitRepository extends EntitySpecificationRepository implements VisitRepo
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->from(Visit::class, 'v');
- if ($filtering->excludeBots()) {
+ if ($filtering->excludeBots) {
$qb->andWhere($qb->expr()->eq('v.potentialBot', 'false'));
}
- $this->applyDatesInline($qb, $filtering->dateRange());
+ $this->applyDatesInline($qb, $filtering->dateRange);
return $qb;
}
diff --git a/module/Core/src/ShortUrl/Model/TagsMode.php b/module/Core/src/ShortUrl/Model/TagsMode.php
new file mode 100644
index 00000000..5ae2d9bb
--- /dev/null
+++ b/module/Core/src/ShortUrl/Model/TagsMode.php
@@ -0,0 +1,13 @@
+tags;
}
- public function tagsMode(): ?string
+ public function tagsMode(): ?TagsMode
{
return $this->tagsMode;
}
diff --git a/module/Core/src/ShortUrl/Persistence/ShortUrlsListFiltering.php b/module/Core/src/ShortUrl/Persistence/ShortUrlsListFiltering.php
index 089915e3..04645126 100644
--- a/module/Core/src/ShortUrl/Persistence/ShortUrlsListFiltering.php
+++ b/module/Core/src/ShortUrl/Persistence/ShortUrlsListFiltering.php
@@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Core\ShortUrl\Persistence;
use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Model\Ordering;
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
+use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
class ShortUrlsListFiltering extends ShortUrlsCountFiltering
@@ -17,7 +18,7 @@ class ShortUrlsListFiltering extends ShortUrlsCountFiltering
private Ordering $orderBy,
?string $searchTerm = null,
array $tags = [],
- ?string $tagsMode = null,
+ ?TagsMode $tagsMode = null,
?DateRange $dateRange = null,
?ApiKey $apiKey = null,
) {
diff --git a/module/Core/src/Tag/Model/TagInfo.php b/module/Core/src/Tag/Model/TagInfo.php
index 6e917399..8a4f196b 100644
--- a/module/Core/src/Tag/Model/TagInfo.php
+++ b/module/Core/src/Tag/Model/TagInfo.php
@@ -8,23 +8,11 @@ use JsonSerializable;
final class TagInfo implements JsonSerializable
{
- public function __construct(private string $tag, private int $shortUrlsCount, private int $visitsCount)
- {
- }
-
- public function tag(): string
- {
- return $this->tag;
- }
-
- public function shortUrlsCount(): int
- {
- return $this->shortUrlsCount;
- }
-
- public function visitsCount(): int
- {
- return $this->visitsCount;
+ public function __construct(
+ public readonly string $tag,
+ public readonly int $shortUrlsCount,
+ public readonly int $visitsCount,
+ ) {
}
public function jsonSerialize(): array
diff --git a/module/Core/src/Tag/Model/TagRenaming.php b/module/Core/src/Tag/Model/TagRenaming.php
index 3bdae21c..9c523b8b 100644
--- a/module/Core/src/Tag/Model/TagRenaming.php
+++ b/module/Core/src/Tag/Model/TagRenaming.php
@@ -10,7 +10,7 @@ use function sprintf;
final class TagRenaming
{
- private function __construct(private string $oldName, private string $newName)
+ private function __construct(public readonly string $oldName, public readonly string $newName)
{
}
@@ -31,16 +31,6 @@ final class TagRenaming
return self::fromNames($payload['oldName'], $payload['newName']);
}
- public function oldName(): string
- {
- return $this->oldName;
- }
-
- public function newName(): string
- {
- return $this->newName;
- }
-
public function nameChanged(): bool
{
return $this->oldName !== $this->newName;
diff --git a/module/Core/src/Tag/Model/TagsListFiltering.php b/module/Core/src/Tag/Model/TagsListFiltering.php
index 8f078788..236dde4a 100644
--- a/module/Core/src/Tag/Model/TagsListFiltering.php
+++ b/module/Core/src/Tag/Model/TagsListFiltering.php
@@ -10,41 +10,16 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
final class TagsListFiltering
{
public function __construct(
- private ?int $limit = null,
- private ?int $offset = null,
- private ?string $searchTerm = null,
- private ?Ordering $orderBy = null,
- private ?ApiKey $apiKey = null,
+ public readonly ?int $limit = null,
+ public readonly ?int $offset = null,
+ public readonly ?string $searchTerm = null,
+ public readonly ?Ordering $orderBy = null,
+ public readonly ?ApiKey $apiKey = null,
) {
}
public static function fromRangeAndParams(int $limit, int $offset, TagsParams $params, ?ApiKey $apiKey): self
{
- return new self($limit, $offset, $params->searchTerm(), $params->orderBy(), $apiKey);
- }
-
- public function limit(): ?int
- {
- return $this->limit;
- }
-
- public function offset(): ?int
- {
- return $this->offset;
- }
-
- public function searchTerm(): ?string
- {
- return $this->searchTerm;
- }
-
- public function orderBy(): ?Ordering
- {
- return $this->orderBy;
- }
-
- public function apiKey(): ?ApiKey
- {
- return $this->apiKey;
+ return new self($limit, $offset, $params->searchTerm, $params->orderBy, $apiKey);
}
}
diff --git a/module/Core/src/Tag/Model/TagsParams.php b/module/Core/src/Tag/Model/TagsParams.php
index 3f40debe..633fd5f2 100644
--- a/module/Core/src/Tag/Model/TagsParams.php
+++ b/module/Core/src/Tag/Model/TagsParams.php
@@ -12,9 +12,9 @@ use function Shlinkio\Shlink\Common\parseOrderBy;
final class TagsParams extends AbstractInfinitePaginableListParams
{
private function __construct(
- private ?string $searchTerm,
- private Ordering $orderBy,
- private bool $withStats,
+ public readonly ?string $searchTerm,
+ public readonly Ordering $orderBy,
+ public readonly bool $withStats,
?int $page,
?int $itemsPerPage,
) {
@@ -31,19 +31,4 @@ final class TagsParams extends AbstractInfinitePaginableListParams
isset($query['itemsPerPage']) ? (int) $query['itemsPerPage'] : null,
);
}
-
- public function searchTerm(): ?string
- {
- return $this->searchTerm;
- }
-
- public function orderBy(): Ordering
- {
- return $this->orderBy;
- }
-
- public function withStats(): bool
- {
- return $this->withStats;
- }
}
diff --git a/module/Core/src/Tag/Paginator/Adapter/AbstractTagsPaginatorAdapter.php b/module/Core/src/Tag/Paginator/Adapter/AbstractTagsPaginatorAdapter.php
index ba6bc78d..ee0086cd 100644
--- a/module/Core/src/Tag/Paginator/Adapter/AbstractTagsPaginatorAdapter.php
+++ b/module/Core/src/Tag/Paginator/Adapter/AbstractTagsPaginatorAdapter.php
@@ -30,7 +30,7 @@ abstract class AbstractTagsPaginatorAdapter implements AdapterInterface
new WithApiKeySpecsEnsuringJoin($this->apiKey),
];
- $searchTerm = $this->params->searchTerm();
+ $searchTerm = $this->params->searchTerm;
if ($searchTerm !== null) {
$conditions[] = Spec::like('name', $searchTerm);
}
diff --git a/module/Core/src/Tag/Paginator/Adapter/TagsPaginatorAdapter.php b/module/Core/src/Tag/Paginator/Adapter/TagsPaginatorAdapter.php
index d6bc0b7b..7d54940e 100644
--- a/module/Core/src/Tag/Paginator/Adapter/TagsPaginatorAdapter.php
+++ b/module/Core/src/Tag/Paginator/Adapter/TagsPaginatorAdapter.php
@@ -15,13 +15,13 @@ class TagsPaginatorAdapter extends AbstractTagsPaginatorAdapter
new WithApiKeySpecsEnsuringJoin($this->apiKey),
Spec::orderBy(
'name', // Ordering by other fields makes no sense here
- $this->params->orderBy()->orderDirection(),
+ $this->params->orderBy->direction,
),
Spec::limit($length),
Spec::offset($offset),
];
- $searchTerm = $this->params->searchTerm();
+ $searchTerm = $this->params->searchTerm;
if ($searchTerm !== null) {
$conditions[] = Spec::like('name', $searchTerm);
}
diff --git a/module/Core/src/Tag/TagService.php b/module/Core/src/Tag/TagService.php
index 40eb413f..b8d7f710 100644
--- a/module/Core/src/Tag/TagService.php
+++ b/module/Core/src/Tag/TagService.php
@@ -49,8 +49,8 @@ class TagService implements TagServiceInterface
private function createPaginator(AdapterInterface $adapter, TagsParams $params): Paginator
{
return (new Paginator($adapter))
- ->setMaxPerPage($params->getItemsPerPage())
- ->setCurrentPage($params->getPage());
+ ->setMaxPerPage($params->itemsPerPage)
+ ->setCurrentPage($params->page);
}
/**
@@ -83,17 +83,17 @@ class TagService implements TagServiceInterface
$repo = $this->em->getRepository(Tag::class);
/** @var Tag|null $tag */
- $tag = $repo->findOneBy(['name' => $renaming->oldName()]);
+ $tag = $repo->findOneBy(['name' => $renaming->oldName]);
if ($tag === null) {
- throw TagNotFoundException::fromTag($renaming->oldName());
+ throw TagNotFoundException::fromTag($renaming->oldName);
}
- $newNameExists = $renaming->nameChanged() && $repo->count(['name' => $renaming->newName()]) > 0;
+ $newNameExists = $renaming->nameChanged() && $repo->count(['name' => $renaming->newName]) > 0;
if ($newNameExists) {
throw TagConflictException::forExistingTag($renaming);
}
- $tag->rename($renaming->newName());
+ $tag->rename($renaming->newName);
$this->em->flush();
return $tag;
diff --git a/module/Core/src/Validation/ShortUrlsParamsInputFilter.php b/module/Core/src/Validation/ShortUrlsParamsInputFilter.php
index 6c0443aa..50953310 100644
--- a/module/Core/src/Validation/ShortUrlsParamsInputFilter.php
+++ b/module/Core/src/Validation/ShortUrlsParamsInputFilter.php
@@ -9,6 +9,7 @@ use Laminas\Validator\InArray;
use Shlinkio\Shlink\Common\Paginator\Paginator;
use Shlinkio\Shlink\Common\Validation;
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
+use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
class ShortUrlsParamsInputFilter extends InputFilter
{
@@ -43,7 +44,7 @@ class ShortUrlsParamsInputFilter extends InputFilter
$tagsMode = $this->createInput(self::TAGS_MODE, false);
$tagsMode->getValidatorChain()->attach(new InArray([
- 'haystack' => [ShortUrlsParams::TAGS_MODE_ALL, ShortUrlsParams::TAGS_MODE_ANY],
+ 'haystack' => [TagsMode::ALL->value, TagsMode::ANY->value],
'strict' => InArray::COMPARE_STRICT,
]));
$this->add($tagsMode);
diff --git a/module/Core/src/Visit/Model/VisitType.php b/module/Core/src/Visit/Model/VisitType.php
new file mode 100644
index 00000000..0c4b8841
--- /dev/null
+++ b/module/Core/src/Visit/Model/VisitType.php
@@ -0,0 +1,16 @@
+visitRepository->countVisitsByDomain(
$this->domain,
new VisitsCountFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
),
);
@@ -38,8 +38,8 @@ class DomainVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapte
return $this->visitRepository->findVisitsByDomain(
$this->domain,
new VisitsListFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
$length,
$offset,
diff --git a/module/Core/src/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapter.php b/module/Core/src/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapter.php
index ba5b6663..5f06ea09 100644
--- a/module/Core/src/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapter.php
+++ b/module/Core/src/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapter.php
@@ -23,8 +23,8 @@ class NonOrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAda
protected function doCount(): int
{
return $this->repo->countNonOrphanVisits(new VisitsCountFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
));
}
@@ -32,8 +32,8 @@ class NonOrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAda
public function getSlice(int $offset, int $length): iterable
{
return $this->repo->findNonOrphanVisits(new VisitsListFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
$length,
$offset,
diff --git a/module/Core/src/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapter.php b/module/Core/src/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapter.php
index 8a47c9d7..f18dbb05 100644
--- a/module/Core/src/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapter.php
+++ b/module/Core/src/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapter.php
@@ -19,16 +19,16 @@ class OrphanVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapte
protected function doCount(): int
{
return $this->repo->countOrphanVisits(new VisitsCountFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
));
}
public function getSlice(int $offset, int $length): iterable
{
return $this->repo->findOrphanVisits(new VisitsListFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
null,
$length,
$offset,
diff --git a/module/Core/src/Visit/Paginator/Adapter/ShortUrlVisitsPaginatorAdapter.php b/module/Core/src/Visit/Paginator/Adapter/ShortUrlVisitsPaginatorAdapter.php
index 2e47fbf8..5169c327 100644
--- a/module/Core/src/Visit/Paginator/Adapter/ShortUrlVisitsPaginatorAdapter.php
+++ b/module/Core/src/Visit/Paginator/Adapter/ShortUrlVisitsPaginatorAdapter.php
@@ -27,8 +27,8 @@ class ShortUrlVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdap
return $this->visitRepository->findVisitsByShortCode(
$this->identifier,
new VisitsListFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
$length,
$offset,
@@ -41,8 +41,8 @@ class ShortUrlVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdap
return $this->visitRepository->countVisitsByShortCode(
$this->identifier,
new VisitsCountFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
),
);
diff --git a/module/Core/src/Visit/Paginator/Adapter/TagVisitsPaginatorAdapter.php b/module/Core/src/Visit/Paginator/Adapter/TagVisitsPaginatorAdapter.php
index 162b6cba..aed79d02 100644
--- a/module/Core/src/Visit/Paginator/Adapter/TagVisitsPaginatorAdapter.php
+++ b/module/Core/src/Visit/Paginator/Adapter/TagVisitsPaginatorAdapter.php
@@ -26,8 +26,8 @@ class TagVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter
return $this->visitRepository->findVisitsByTag(
$this->tag,
new VisitsListFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
$length,
$offset,
@@ -40,8 +40,8 @@ class TagVisitsPaginatorAdapter extends AbstractCacheableCountPaginatorAdapter
return $this->visitRepository->countVisitsByTag(
$this->tag,
new VisitsCountFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
),
);
diff --git a/module/Core/src/Visit/Persistence/VisitsCountFiltering.php b/module/Core/src/Visit/Persistence/VisitsCountFiltering.php
index 140ec9b9..f839a945 100644
--- a/module/Core/src/Visit/Persistence/VisitsCountFiltering.php
+++ b/module/Core/src/Visit/Persistence/VisitsCountFiltering.php
@@ -10,9 +10,9 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
class VisitsCountFiltering
{
public function __construct(
- private ?DateRange $dateRange = null,
- private bool $excludeBots = false,
- private ?ApiKey $apiKey = null,
+ public readonly ?DateRange $dateRange = null,
+ public readonly bool $excludeBots = false,
+ public readonly ?ApiKey $apiKey = null,
) {
}
@@ -20,19 +20,4 @@ class VisitsCountFiltering
{
return new self(null, false, $apiKey);
}
-
- public function dateRange(): ?DateRange
- {
- return $this->dateRange;
- }
-
- public function excludeBots(): bool
- {
- return $this->excludeBots;
- }
-
- public function apiKey(): ?ApiKey
- {
- return $this->apiKey;
- }
}
diff --git a/module/Core/src/Visit/Persistence/VisitsListFiltering.php b/module/Core/src/Visit/Persistence/VisitsListFiltering.php
index b17964a6..747a3ce0 100644
--- a/module/Core/src/Visit/Persistence/VisitsListFiltering.php
+++ b/module/Core/src/Visit/Persistence/VisitsListFiltering.php
@@ -13,19 +13,9 @@ final class VisitsListFiltering extends VisitsCountFiltering
?DateRange $dateRange = null,
bool $excludeBots = false,
?ApiKey $apiKey = null,
- private ?int $limit = null,
- private ?int $offset = null,
+ public readonly ?int $limit = null,
+ public readonly ?int $offset = null,
) {
parent::__construct($dateRange, $excludeBots, $apiKey);
}
-
- public function limit(): ?int
- {
- return $this->limit;
- }
-
- public function offset(): ?int
- {
- return $this->offset;
- }
}
diff --git a/module/Core/src/Visit/Spec/CountOfNonOrphanVisits.php b/module/Core/src/Visit/Spec/CountOfNonOrphanVisits.php
index 52be52a8..a7b2a1d6 100644
--- a/module/Core/src/Visit/Spec/CountOfNonOrphanVisits.php
+++ b/module/Core/src/Visit/Spec/CountOfNonOrphanVisits.php
@@ -22,14 +22,14 @@ class CountOfNonOrphanVisits extends BaseSpecification
{
$conditions = [
Spec::isNotNull('shortUrl'),
- new InDateRange($this->filtering->dateRange()),
+ new InDateRange($this->filtering->dateRange),
];
- if ($this->filtering->excludeBots()) {
+ if ($this->filtering->excludeBots) {
$conditions[] = Spec::eq('potentialBot', false);
}
- $apiKey = $this->filtering->apiKey();
+ $apiKey = $this->filtering->apiKey;
if ($apiKey !== null) {
$conditions[] = new WithApiKeySpecsEnsuringJoin($apiKey, 'shortUrl');
}
diff --git a/module/Core/src/Visit/Spec/CountOfOrphanVisits.php b/module/Core/src/Visit/Spec/CountOfOrphanVisits.php
index d8e6b2d2..106350c6 100644
--- a/module/Core/src/Visit/Spec/CountOfOrphanVisits.php
+++ b/module/Core/src/Visit/Spec/CountOfOrphanVisits.php
@@ -21,10 +21,10 @@ class CountOfOrphanVisits extends BaseSpecification
{
$conditions = [
Spec::isNull('shortUrl'),
- new InDateRange($this->filtering->dateRange()),
+ new InDateRange($this->filtering->dateRange),
];
- if ($this->filtering->excludeBots()) {
+ if ($this->filtering->excludeBots) {
$conditions[] = Spec::eq('potentialBot', false);
}
diff --git a/module/Core/src/Visit/Transformer/OrphanVisitDataTransformer.php b/module/Core/src/Visit/Transformer/OrphanVisitDataTransformer.php
index 9f4842f5..c9d30b8d 100644
--- a/module/Core/src/Visit/Transformer/OrphanVisitDataTransformer.php
+++ b/module/Core/src/Visit/Transformer/OrphanVisitDataTransformer.php
@@ -17,7 +17,7 @@ class OrphanVisitDataTransformer implements DataTransformerInterface
{
$serializedVisit = $visit->jsonSerialize();
$serializedVisit['visitedUrl'] = $visit->visitedUrl();
- $serializedVisit['type'] = $visit->type();
+ $serializedVisit['type'] = $visit->type()->value;
return $serializedVisit;
}
diff --git a/module/Core/src/Visit/VisitsStatsHelper.php b/module/Core/src/Visit/VisitsStatsHelper.php
index 007ed334..4f19103f 100644
--- a/module/Core/src/Visit/VisitsStatsHelper.php
+++ b/module/Core/src/Visit/VisitsStatsHelper.php
@@ -129,8 +129,8 @@ class VisitsStatsHelper implements VisitsStatsHelperInterface
private function createPaginator(AdapterInterface $adapter, VisitsParams $params): Paginator
{
$paginator = new Paginator($adapter);
- $paginator->setMaxPerPage($params->getItemsPerPage())
- ->setCurrentPage($params->getPage());
+ $paginator->setMaxPerPage($params->itemsPerPage)
+ ->setCurrentPage($params->page);
return $paginator;
}
diff --git a/module/Core/src/Visit/VisitsTracker.php b/module/Core/src/Visit/VisitsTracker.php
index f4e5bf92..3aef46df 100644
--- a/module/Core/src/Visit/VisitsTracker.php
+++ b/module/Core/src/Visit/VisitsTracker.php
@@ -72,6 +72,6 @@ class VisitsTracker implements VisitsTrackerInterface
$this->em->persist($visit);
$this->em->flush();
- $this->eventDispatcher->dispatch(new UrlVisited($visit->getId(), $visitor->getRemoteAddress()));
+ $this->eventDispatcher->dispatch(new UrlVisited($visit->getId(), $visitor->remoteAddress));
}
}
diff --git a/module/Core/test-db/Repository/ShortUrlRepositoryTest.php b/module/Core/test-db/Repository/ShortUrlRepositoryTest.php
index 4ad89629..05c65ee3 100644
--- a/module/Core/test-db/Repository/ShortUrlRepositoryTest.php
+++ b/module/Core/test-db/Repository/ShortUrlRepositoryTest.php
@@ -14,9 +14,9 @@ use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Model\Ordering;
use Shlinkio\Shlink\Core\Model\ShortUrlIdentifier;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
-use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepository;
+use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsCountFiltering;
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsListFiltering;
use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver;
@@ -227,7 +227,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
Ordering::emptyInstance(),
null,
['foo', 'bar'],
- ShortUrlsParams::TAGS_MODE_ANY,
+ TagsMode::ANY,
)));
self::assertCount(1, $this->repo->findList(new ShortUrlsListFiltering(
null,
@@ -235,15 +235,11 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
Ordering::emptyInstance(),
null,
['foo', 'bar'],
- ShortUrlsParams::TAGS_MODE_ALL,
+ TagsMode::ALL,
)));
self::assertEquals(5, $this->repo->countList(new ShortUrlsCountFiltering(null, ['foo', 'bar'])));
- self::assertEquals(5, $this->repo->countList(
- new ShortUrlsCountFiltering(null, ['foo', 'bar'], ShortUrlsParams::TAGS_MODE_ANY),
- ));
- self::assertEquals(1, $this->repo->countList(
- new ShortUrlsCountFiltering(null, ['foo', 'bar'], ShortUrlsParams::TAGS_MODE_ALL),
- ));
+ self::assertEquals(5, $this->repo->countList(new ShortUrlsCountFiltering(null, ['foo', 'bar'], TagsMode::ANY)));
+ self::assertEquals(1, $this->repo->countList(new ShortUrlsCountFiltering(null, ['foo', 'bar'], TagsMode::ALL)));
self::assertCount(4, $this->repo->findList(
new ShortUrlsListFiltering(null, null, Ordering::emptyInstance(), null, ['bar', 'baz']),
@@ -254,7 +250,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
Ordering::emptyInstance(),
null,
['bar', 'baz'],
- ShortUrlsParams::TAGS_MODE_ANY,
+ TagsMode::ANY,
)));
self::assertCount(2, $this->repo->findList(new ShortUrlsListFiltering(
null,
@@ -262,14 +258,14 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
Ordering::emptyInstance(),
null,
['bar', 'baz'],
- ShortUrlsParams::TAGS_MODE_ALL,
+ TagsMode::ALL,
)));
self::assertEquals(4, $this->repo->countList(new ShortUrlsCountFiltering(null, ['bar', 'baz'])));
self::assertEquals(4, $this->repo->countList(
- new ShortUrlsCountFiltering(null, ['bar', 'baz'], ShortUrlsParams::TAGS_MODE_ANY),
+ new ShortUrlsCountFiltering(null, ['bar', 'baz'], TagsMode::ANY),
));
self::assertEquals(2, $this->repo->countList(
- new ShortUrlsCountFiltering(null, ['bar', 'baz'], ShortUrlsParams::TAGS_MODE_ALL),
+ new ShortUrlsCountFiltering(null, ['bar', 'baz'], TagsMode::ALL),
));
self::assertCount(5, $this->repo->findList(
@@ -281,7 +277,7 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
Ordering::emptyInstance(),
null,
['foo', 'bar', 'baz'],
- ShortUrlsParams::TAGS_MODE_ANY,
+ TagsMode::ANY,
)));
self::assertCount(0, $this->repo->findList(new ShortUrlsListFiltering(
null,
@@ -289,14 +285,14 @@ class ShortUrlRepositoryTest extends DatabaseTestCase
Ordering::emptyInstance(),
null,
['foo', 'bar', 'baz'],
- ShortUrlsParams::TAGS_MODE_ALL,
+ TagsMode::ALL,
)));
self::assertEquals(5, $this->repo->countList(new ShortUrlsCountFiltering(null, ['foo', 'bar', 'baz'])));
self::assertEquals(5, $this->repo->countList(
- new ShortUrlsCountFiltering(null, ['foo', 'bar', 'baz'], ShortUrlsParams::TAGS_MODE_ANY),
+ new ShortUrlsCountFiltering(null, ['foo', 'bar', 'baz'], TagsMode::ANY),
));
self::assertEquals(0, $this->repo->countList(
- new ShortUrlsCountFiltering(null, ['foo', 'bar', 'baz'], ShortUrlsParams::TAGS_MODE_ALL),
+ new ShortUrlsCountFiltering(null, ['foo', 'bar', 'baz'], TagsMode::ALL),
));
}
diff --git a/module/Core/test-db/Repository/TagRepositoryTest.php b/module/Core/test-db/Repository/TagRepositoryTest.php
index fe544376..87cd7280 100644
--- a/module/Core/test-db/Repository/TagRepositoryTest.php
+++ b/module/Core/test-db/Repository/TagRepositoryTest.php
@@ -64,7 +64,7 @@ class TagRepositoryTest extends DatabaseTestCase
$this->getEntityManager()->persist(new Tag($name));
}
- $apiKey = $filtering?->apiKey();
+ $apiKey = $filtering?->apiKey;
if ($apiKey !== null) {
$this->getEntityManager()->persist($apiKey);
}
@@ -101,9 +101,9 @@ class TagRepositoryTest extends DatabaseTestCase
self::assertCount(count($expectedList), $result);
foreach ($expectedList as $index => [$tag, $shortUrlsCount, $visitsCount]) {
- self::assertEquals($shortUrlsCount, $result[$index]->shortUrlsCount());
- self::assertEquals($visitsCount, $result[$index]->visitsCount());
- self::assertEquals($tag, $result[$index]->tag());
+ self::assertEquals($shortUrlsCount, $result[$index]->shortUrlsCount);
+ self::assertEquals($visitsCount, $result[$index]->visitsCount);
+ self::assertEquals($tag, $result[$index]->tag);
}
}
diff --git a/module/Core/test/Action/PixelActionTest.php b/module/Core/test/Action/PixelActionTest.php
index 3eb1ad79..fdd291a5 100644
--- a/module/Core/test/Action/PixelActionTest.php
+++ b/module/Core/test/Action/PixelActionTest.php
@@ -37,9 +37,10 @@ class PixelActionTest extends TestCase
public function imageIsReturned(): void
{
$shortCode = 'abc123';
- $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))->willReturn(
- ShortUrl::withLongUrl('http://domain.com/foo/bar'),
- )->shouldBeCalledOnce();
+ $this->urlResolver->resolveEnabledShortUrl(
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode, ''),
+ )->willReturn(ShortUrl::withLongUrl('http://domain.com/foo/bar'))
+ ->shouldBeCalledOnce();
$this->requestTracker->trackIfApplicable(Argument::cetera())->shouldBeCalledOnce();
$request = (new ServerRequest())->withAttribute('shortCode', $shortCode);
diff --git a/module/Core/test/Action/QrCodeActionTest.php b/module/Core/test/Action/QrCodeActionTest.php
index 419febec..fb9e4e6a 100644
--- a/module/Core/test/Action/QrCodeActionTest.php
+++ b/module/Core/test/Action/QrCodeActionTest.php
@@ -59,7 +59,7 @@ class QrCodeActionTest extends TestCase
public function aNotFoundShortCodeWillDelegateIntoNextMiddleware(): void
{
$shortCode = 'abc123';
- $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))
+ $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode, ''))
->willThrow(ShortUrlNotFoundException::class)
->shouldBeCalledOnce();
$delegate = $this->prophesize(RequestHandlerInterface::class);
@@ -74,7 +74,7 @@ class QrCodeActionTest extends TestCase
public function aCorrectRequestReturnsTheQrCodeResponse(): void
{
$shortCode = 'abc123';
- $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))
+ $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode, ''))
->willReturn(ShortUrl::createEmpty())
->shouldBeCalledOnce();
$delegate = $this->prophesize(RequestHandlerInterface::class);
@@ -100,7 +100,7 @@ class QrCodeActionTest extends TestCase
): void {
$this->options->setFromArray(['format' => $defaultFormat]);
$code = 'abc123';
- $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($code, ''))->willReturn(
+ $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($code, ''))->willReturn(
ShortUrl::createEmpty(),
);
$delegate = $this->prophesize(RequestHandlerInterface::class);
@@ -134,7 +134,7 @@ class QrCodeActionTest extends TestCase
): void {
$this->options->setFromArray($defaults);
$code = 'abc123';
- $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($code, ''))->willReturn(
+ $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($code, ''))->willReturn(
ShortUrl::createEmpty(),
);
$delegate = $this->prophesize(RequestHandlerInterface::class);
@@ -214,7 +214,7 @@ class QrCodeActionTest extends TestCase
->withQueryParams(['size' => 250, 'roundBlockSize' => $roundBlockSize])
->withAttribute('shortCode', $code);
- $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($code, ''))->willReturn(
+ $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($code, ''))->willReturn(
ShortUrl::withLongUrl('https://shlink.io'),
);
$delegate = $this->prophesize(RequestHandlerInterface::class);
diff --git a/module/Core/test/Action/RedirectActionTest.php b/module/Core/test/Action/RedirectActionTest.php
index b3017fad..cde2b9aa 100644
--- a/module/Core/test/Action/RedirectActionTest.php
+++ b/module/Core/test/Action/RedirectActionTest.php
@@ -54,7 +54,7 @@ class RedirectActionTest extends TestCase
$shortCode = 'abc123';
$shortUrl = ShortUrl::withLongUrl(self::LONG_URL);
$shortCodeToUrl = $this->urlResolver->resolveEnabledShortUrl(
- new ShortUrlIdentifier($shortCode, ''),
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode, ''),
)->willReturn($shortUrl);
$track = $this->requestTracker->trackIfApplicable(Argument::cetera())->will(function (): void {
});
@@ -74,7 +74,7 @@ class RedirectActionTest extends TestCase
public function nextMiddlewareIsInvokedIfLongUrlIsNotFound(): void
{
$shortCode = 'abc123';
- $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode, ''))
+ $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode, ''))
->willThrow(ShortUrlNotFoundException::class)
->shouldBeCalledOnce();
$this->requestTracker->trackIfApplicable(Argument::cetera())->shouldNotBeCalled();
diff --git a/module/Core/test/Config/EnvVarsTest.php b/module/Core/test/Config/EnvVarsTest.php
index 51a7a088..6d4b1394 100644
--- a/module/Core/test/Config/EnvVarsTest.php
+++ b/module/Core/test/Config/EnvVarsTest.php
@@ -6,7 +6,6 @@ namespace ShlinkioTest\Shlink\Core\Config;
use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\Config\EnvVars;
-use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
use function putenv;
@@ -14,92 +13,14 @@ class EnvVarsTest extends TestCase
{
protected function setUp(): void
{
- putenv(EnvVars::BASE_PATH . '=the_base_path');
- putenv(EnvVars::DB_NAME . '=shlink');
+ putenv(EnvVars::BASE_PATH->value . '=the_base_path');
+ putenv(EnvVars::DB_NAME->value . '=shlink');
}
protected function tearDown(): void
{
- putenv(EnvVars::BASE_PATH . '=');
- putenv(EnvVars::DB_NAME . '=');
- }
-
- /** @test */
- public function casesReturnsTheSameListEveryTime(): void
- {
- $list = EnvVars::cases();
- self::assertSame($list, EnvVars::cases());
- self::assertSame([
- EnvVars::DELETE_SHORT_URL_THRESHOLD,
- EnvVars::DB_DRIVER,
- EnvVars::DB_NAME,
- EnvVars::DB_USER,
- EnvVars::DB_PASSWORD,
- EnvVars::DB_HOST,
- EnvVars::DB_UNIX_SOCKET,
- EnvVars::DB_PORT,
- EnvVars::GEOLITE_LICENSE_KEY,
- EnvVars::REDIS_SERVERS,
- EnvVars::REDIS_SENTINEL_SERVICE,
- EnvVars::MERCURE_PUBLIC_HUB_URL,
- EnvVars::MERCURE_INTERNAL_HUB_URL,
- EnvVars::MERCURE_JWT_SECRET,
- EnvVars::DEFAULT_QR_CODE_SIZE,
- EnvVars::DEFAULT_QR_CODE_MARGIN,
- EnvVars::DEFAULT_QR_CODE_FORMAT,
- EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION,
- EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE,
- EnvVars::RABBITMQ_ENABLED,
- EnvVars::RABBITMQ_HOST,
- EnvVars::RABBITMQ_PORT,
- EnvVars::RABBITMQ_USER,
- EnvVars::RABBITMQ_PASSWORD,
- EnvVars::RABBITMQ_VHOST,
- EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT,
- EnvVars::DEFAULT_REGULAR_404_REDIRECT,
- EnvVars::DEFAULT_BASE_URL_REDIRECT,
- EnvVars::REDIRECT_STATUS_CODE,
- EnvVars::REDIRECT_CACHE_LIFETIME,
- EnvVars::BASE_PATH,
- EnvVars::PORT,
- EnvVars::TASK_WORKER_NUM,
- EnvVars::WEB_WORKER_NUM,
- EnvVars::ANONYMIZE_REMOTE_ADDR,
- EnvVars::TRACK_ORPHAN_VISITS,
- EnvVars::DISABLE_TRACK_PARAM,
- EnvVars::DISABLE_TRACKING,
- EnvVars::DISABLE_IP_TRACKING,
- EnvVars::DISABLE_REFERRER_TRACKING,
- EnvVars::DISABLE_UA_TRACKING,
- EnvVars::DISABLE_TRACKING_FROM,
- EnvVars::DEFAULT_SHORT_CODES_LENGTH,
- EnvVars::IS_HTTPS_ENABLED,
- EnvVars::DEFAULT_DOMAIN,
- EnvVars::AUTO_RESOLVE_TITLES,
- EnvVars::REDIRECT_APPEND_EXTRA_PATH,
- EnvVars::TIMEZONE,
- EnvVars::VISITS_WEBHOOKS,
- EnvVars::NOTIFY_ORPHAN_VISITS_TO_WEBHOOKS,
- ], $list);
- }
-
- /**
- * @test
- * @dataProvider provideInvalidEnvVars
- */
- public function exceptionIsThrownWhenTryingToLoadInvalidEnvVar(string $envVar): void
- {
- $this->expectException(InvalidArgumentException::class);
- $this->expectExceptionMessage('Invalid env var: "' . $envVar . '"');
-
- EnvVars::{$envVar}();
- }
-
- public function provideInvalidEnvVars(): iterable
- {
- yield 'foo' => ['foo'];
- yield 'bar' => ['bar'];
- yield 'invalid' => ['invalid'];
+ putenv(EnvVars::BASE_PATH->value . '=');
+ putenv(EnvVars::DB_NAME->value . '=');
}
/**
@@ -113,10 +34,10 @@ class EnvVarsTest extends TestCase
public function provideExistingEnvVars(): iterable
{
- yield 'DB_NAME' => [EnvVars::DB_NAME(), true];
- yield 'BASE_PATH' => [EnvVars::BASE_PATH(), true];
- yield 'DB_DRIVER' => [EnvVars::DB_DRIVER(), false];
- yield 'DEFAULT_REGULAR_404_REDIRECT' => [EnvVars::DEFAULT_REGULAR_404_REDIRECT(), false];
+ yield 'DB_NAME' => [EnvVars::DB_NAME, true];
+ yield 'BASE_PATH' => [EnvVars::BASE_PATH, true];
+ yield 'DB_DRIVER' => [EnvVars::DB_DRIVER, false];
+ yield 'DEFAULT_REGULAR_404_REDIRECT' => [EnvVars::DEFAULT_REGULAR_404_REDIRECT, false];
}
/**
@@ -130,11 +51,11 @@ class EnvVarsTest extends TestCase
public function provideEnvVarsValues(): iterable
{
- yield 'DB_NAME without default' => [EnvVars::DB_NAME(), 'shlink', null];
- yield 'DB_NAME with default' => [EnvVars::DB_NAME(), 'shlink', 'foobar'];
- yield 'BASE_PATH without default' => [EnvVars::BASE_PATH(), 'the_base_path', null];
- yield 'BASE_PATH with default' => [EnvVars::BASE_PATH(), 'the_base_path', 'foobar'];
- yield 'DB_DRIVER without default' => [EnvVars::DB_DRIVER(), null, null];
- yield 'DB_DRIVER with default' => [EnvVars::DB_DRIVER(), 'foobar', 'foobar'];
+ yield 'DB_NAME without default' => [EnvVars::DB_NAME, 'shlink', null];
+ yield 'DB_NAME with default' => [EnvVars::DB_NAME, 'shlink', 'foobar'];
+ yield 'BASE_PATH without default' => [EnvVars::BASE_PATH, 'the_base_path', null];
+ yield 'BASE_PATH with default' => [EnvVars::BASE_PATH, 'the_base_path', 'foobar'];
+ yield 'DB_DRIVER without default' => [EnvVars::DB_DRIVER, null, null];
+ yield 'DB_DRIVER with default' => [EnvVars::DB_DRIVER, 'foobar', 'foobar'];
}
}
diff --git a/module/Core/test/EventDispatcher/NotifyVisitToMercureTest.php b/module/Core/test/EventDispatcher/NotifyVisitToMercureTest.php
index 0b863b69..a06eaaa1 100644
--- a/module/Core/test/EventDispatcher/NotifyVisitToMercureTest.php
+++ b/module/Core/test/EventDispatcher/NotifyVisitToMercureTest.php
@@ -17,6 +17,7 @@ use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated;
use Shlinkio\Shlink\Core\EventDispatcher\NotifyVisitToMercure;
use Shlinkio\Shlink\Core\Mercure\MercureUpdatesGeneratorInterface;
use Shlinkio\Shlink\Core\Model\Visitor;
+use Shlinkio\Shlink\Core\Visit\Model\VisitType;
use Symfony\Component\Mercure\HubInterface;
use Symfony\Component\Mercure\Update;
@@ -160,8 +161,8 @@ class NotifyVisitToMercureTest extends TestCase
{
$visitor = Visitor::emptyInstance();
- yield Visit::TYPE_REGULAR_404 => [Visit::forRegularNotFound($visitor)];
- yield Visit::TYPE_INVALID_SHORT_URL => [Visit::forInvalidShortUrl($visitor)];
- yield Visit::TYPE_BASE_URL => [Visit::forBasePath($visitor)];
+ yield VisitType::REGULAR_404->value => [Visit::forRegularNotFound($visitor)];
+ yield VisitType::INVALID_SHORT_URL->value => [Visit::forInvalidShortUrl($visitor)];
+ yield VisitType::BASE_URL->value => [Visit::forBasePath($visitor)];
}
}
diff --git a/module/Core/test/Exception/ShortUrlNotFoundExceptionTest.php b/module/Core/test/Exception/ShortUrlNotFoundExceptionTest.php
index ea4e606d..e86a63cb 100644
--- a/module/Core/test/Exception/ShortUrlNotFoundExceptionTest.php
+++ b/module/Core/test/Exception/ShortUrlNotFoundExceptionTest.php
@@ -24,7 +24,7 @@ class ShortUrlNotFoundExceptionTest extends TestCase
$expectedAdditional['domain'] = $domain;
}
- $e = ShortUrlNotFoundException::fromNotFound(new ShortUrlIdentifier($shortCode, $domain));
+ $e = ShortUrlNotFoundException::fromNotFound(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode, $domain));
self::assertEquals($expectedMessage, $e->getMessage());
self::assertEquals($expectedMessage, $e->getDetail());
diff --git a/module/Core/test/Mercure/MercureUpdatesGeneratorTest.php b/module/Core/test/Mercure/MercureUpdatesGeneratorTest.php
index 14378b4f..779ec351 100644
--- a/module/Core/test/Mercure/MercureUpdatesGeneratorTest.php
+++ b/module/Core/test/Mercure/MercureUpdatesGeneratorTest.php
@@ -12,6 +12,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Model\Visitor;
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier;
use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer;
+use Shlinkio\Shlink\Core\Visit\Model\VisitType;
use Shlinkio\Shlink\Core\Visit\Transformer\OrphanVisitDataTransformer;
use function Shlinkio\Shlink\Common\json_decode;
@@ -95,7 +96,7 @@ class MercureUpdatesGeneratorTest extends TestCase
'date' => $orphanVisit->getDate()->toAtomString(),
'potentialBot' => false,
'visitedUrl' => $orphanVisit->visitedUrl(),
- 'type' => $orphanVisit->type(),
+ 'type' => $orphanVisit->type()->value,
],
], json_decode($update->getData()));
}
@@ -104,8 +105,8 @@ class MercureUpdatesGeneratorTest extends TestCase
{
$visitor = Visitor::emptyInstance();
- yield Visit::TYPE_REGULAR_404 => [Visit::forRegularNotFound($visitor)];
- yield Visit::TYPE_INVALID_SHORT_URL => [Visit::forInvalidShortUrl($visitor)];
- yield Visit::TYPE_BASE_URL => [Visit::forBasePath($visitor)];
+ yield VisitType::REGULAR_404->value => [Visit::forRegularNotFound($visitor)];
+ yield VisitType::INVALID_SHORT_URL->value => [Visit::forInvalidShortUrl($visitor)];
+ yield VisitType::BASE_URL->value => [Visit::forBasePath($visitor)];
}
}
diff --git a/module/Core/test/Model/VisitorTest.php b/module/Core/test/Model/VisitorTest.php
index 50c277c4..92a46a16 100644
--- a/module/Core/test/Model/VisitorTest.php
+++ b/module/Core/test/Model/VisitorTest.php
@@ -24,9 +24,9 @@ class VisitorTest extends TestCase
$visitor = new Visitor(...$params);
['userAgent' => $userAgent, 'referer' => $referer, 'remoteAddress' => $remoteAddress] = $expected;
- self::assertEquals($userAgent, $visitor->getUserAgent());
- self::assertEquals($referer, $visitor->getReferer());
- self::assertEquals($remoteAddress, $visitor->getRemoteAddress());
+ self::assertEquals($userAgent, $visitor->userAgent);
+ self::assertEquals($referer, $visitor->referer);
+ self::assertEquals($remoteAddress, $visitor->remoteAddress);
}
public function provideParams(): iterable
@@ -89,11 +89,11 @@ class VisitorTest extends TestCase
]));
self::assertNotSame($visitor, $normalizedVisitor);
- self::assertEmpty($normalizedVisitor->getUserAgent());
- self::assertNotEmpty($visitor->getUserAgent());
- self::assertEmpty($normalizedVisitor->getReferer());
- self::assertNotEmpty($visitor->getReferer());
- self::assertNull($normalizedVisitor->getRemoteAddress());
- self::assertNotNull($visitor->getRemoteAddress());
+ self::assertEmpty($normalizedVisitor->userAgent);
+ self::assertNotEmpty($visitor->userAgent);
+ self::assertEmpty($normalizedVisitor->referer);
+ self::assertNotEmpty($visitor->referer);
+ self::assertNull($normalizedVisitor->remoteAddress);
+ self::assertNotNull($visitor->remoteAddress);
}
}
diff --git a/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php b/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php
index 6c03d7b5..cd4d6193 100644
--- a/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php
+++ b/module/Core/test/Service/ShortUrl/DeleteShortUrlServiceTest.php
@@ -55,7 +55,7 @@ class DeleteShortUrlServiceTest extends TestCase
$this->shortCode,
));
- $service->deleteByShortCode(new ShortUrlIdentifier($this->shortCode));
+ $service->deleteByShortCode(ShortUrlIdentifier::fromShortCodeAndDomain($this->shortCode));
}
/** @test */
@@ -66,7 +66,7 @@ class DeleteShortUrlServiceTest extends TestCase
$remove = $this->em->remove(Argument::type(ShortUrl::class))->willReturn(null);
$flush = $this->em->flush()->willReturn(null);
- $service->deleteByShortCode(new ShortUrlIdentifier($this->shortCode), true);
+ $service->deleteByShortCode(ShortUrlIdentifier::fromShortCodeAndDomain($this->shortCode), true);
$remove->shouldHaveBeenCalledOnce();
$flush->shouldHaveBeenCalledOnce();
@@ -80,7 +80,7 @@ class DeleteShortUrlServiceTest extends TestCase
$remove = $this->em->remove(Argument::type(ShortUrl::class))->willReturn(null);
$flush = $this->em->flush()->willReturn(null);
- $service->deleteByShortCode(new ShortUrlIdentifier($this->shortCode));
+ $service->deleteByShortCode(ShortUrlIdentifier::fromShortCodeAndDomain($this->shortCode));
$remove->shouldHaveBeenCalledOnce();
$flush->shouldHaveBeenCalledOnce();
@@ -94,7 +94,7 @@ class DeleteShortUrlServiceTest extends TestCase
$remove = $this->em->remove(Argument::type(ShortUrl::class))->willReturn(null);
$flush = $this->em->flush()->willReturn(null);
- $service->deleteByShortCode(new ShortUrlIdentifier($this->shortCode));
+ $service->deleteByShortCode(ShortUrlIdentifier::fromShortCodeAndDomain($this->shortCode));
$remove->shouldHaveBeenCalledOnce();
$flush->shouldHaveBeenCalledOnce();
diff --git a/module/Core/test/Service/ShortUrl/ShortUrlResolverTest.php b/module/Core/test/Service/ShortUrl/ShortUrlResolverTest.php
index 70857e5e..bdccfa3f 100644
--- a/module/Core/test/Service/ShortUrl/ShortUrlResolverTest.php
+++ b/module/Core/test/Service/ShortUrl/ShortUrlResolverTest.php
@@ -91,7 +91,7 @@ class ShortUrlResolverTest extends TestCase
)->willReturn($shortUrl);
$getRepo = $this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal());
- $result = $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode));
+ $result = $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode));
self::assertSame($shortUrl, $result);
$findOneByShortCode->shouldHaveBeenCalledOnce();
@@ -116,7 +116,7 @@ class ShortUrlResolverTest extends TestCase
$findOneByShortCode->shouldBeCalledOnce();
$getRepo->shouldBeCalledOnce();
- $this->urlResolver->resolveEnabledShortUrl(new ShortUrlIdentifier($shortCode));
+ $this->urlResolver->resolveEnabledShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode));
}
public function provideDisabledShortUrls(): iterable
diff --git a/module/Core/test/Service/ShortUrlServiceTest.php b/module/Core/test/Service/ShortUrlServiceTest.php
index b07d4df9..90000423 100644
--- a/module/Core/test/Service/ShortUrlServiceTest.php
+++ b/module/Core/test/Service/ShortUrlServiceTest.php
@@ -88,7 +88,7 @@ class ShortUrlServiceTest extends TestCase
$shortUrl = ShortUrl::withLongUrl($originalLongUrl);
$findShortUrl = $this->urlResolver->resolveShortUrl(
- new ShortUrlIdentifier('abc123'),
+ ShortUrlIdentifier::fromShortCodeAndDomain('abc123'),
$apiKey,
)->willReturn($shortUrl);
$flush = $this->em->flush()->willReturn(null);
@@ -97,7 +97,11 @@ class ShortUrlServiceTest extends TestCase
$shortUrlEdit,
);
- $result = $this->service->updateShortUrl(new ShortUrlIdentifier('abc123'), $shortUrlEdit, $apiKey);
+ $result = $this->service->updateShortUrl(
+ ShortUrlIdentifier::fromShortCodeAndDomain('abc123'),
+ $shortUrlEdit,
+ $apiKey,
+ );
self::assertSame($shortUrl, $result);
self::assertEquals($shortUrlEdit->validSince(), $shortUrl->getValidSince());
diff --git a/module/Core/test/ShortUrl/Paginator/Adapter/ShortUrlRepositoryAdapterTest.php b/module/Core/test/ShortUrl/Paginator/Adapter/ShortUrlRepositoryAdapterTest.php
index 336526b1..2675b04a 100644
--- a/module/Core/test/ShortUrl/Paginator/Adapter/ShortUrlRepositoryAdapterTest.php
+++ b/module/Core/test/ShortUrl/Paginator/Adapter/ShortUrlRepositoryAdapterTest.php
@@ -10,6 +10,7 @@ use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Shlinkio\Shlink\Core\Model\ShortUrlsParams;
use Shlinkio\Shlink\Core\Repository\ShortUrlRepositoryInterface;
+use Shlinkio\Shlink\Core\ShortUrl\Model\TagsMode;
use Shlinkio\Shlink\Core\ShortUrl\Paginator\Adapter\ShortUrlRepositoryAdapter;
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsCountFiltering;
use Shlinkio\Shlink\Core\ShortUrl\Persistence\ShortUrlsListFiltering;
@@ -49,7 +50,7 @@ class ShortUrlRepositoryAdapterTest extends TestCase
$dateRange = $params->dateRange();
$this->repo->findList(
- new ShortUrlsListFiltering(10, 5, $orderBy, $searchTerm, $tags, ShortUrlsParams::TAGS_MODE_ANY, $dateRange),
+ new ShortUrlsListFiltering(10, 5, $orderBy, $searchTerm, $tags, TagsMode::ANY, $dateRange),
)->shouldBeCalledOnce();
$adapter->getSlice(5, 10);
}
@@ -75,7 +76,7 @@ class ShortUrlRepositoryAdapterTest extends TestCase
$dateRange = $params->dateRange();
$this->repo->countList(
- new ShortUrlsCountFiltering($searchTerm, $tags, ShortUrlsParams::TAGS_MODE_ANY, $dateRange, $apiKey),
+ new ShortUrlsCountFiltering($searchTerm, $tags, TagsMode::ANY, $dateRange, $apiKey),
)->shouldBeCalledOnce();
$adapter->getNbResults();
}
diff --git a/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php b/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php
index 4c4c00e5..ba1d2767 100644
--- a/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php
+++ b/module/Core/test/Visit/Paginator/Adapter/NonOrphanVisitsPaginatorAdapterTest.php
@@ -39,7 +39,7 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase
{
$expectedCount = 5;
$repoCount = $this->repo->countNonOrphanVisits(
- new VisitsCountFiltering($this->params->getDateRange(), $this->params->excludeBots(), $this->apiKey),
+ new VisitsCountFiltering($this->params->dateRange, $this->params->excludeBots, $this->apiKey),
)->willReturn($expectedCount);
$result = $this->adapter->getNbResults();
@@ -57,8 +57,8 @@ class NonOrphanVisitsPaginatorAdapterTest extends TestCase
$visitor = Visitor::emptyInstance();
$list = [Visit::forRegularNotFound($visitor), Visit::forInvalidShortUrl($visitor)];
$repoFind = $this->repo->findNonOrphanVisits(new VisitsListFiltering(
- $this->params->getDateRange(),
- $this->params->excludeBots(),
+ $this->params->dateRange,
+ $this->params->excludeBots,
$this->apiKey,
$limit,
$offset,
diff --git a/module/Core/test/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapterTest.php b/module/Core/test/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapterTest.php
index 0ea91f29..6709c538 100644
--- a/module/Core/test/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapterTest.php
+++ b/module/Core/test/Visit/Paginator/Adapter/OrphanVisitsPaginatorAdapterTest.php
@@ -35,7 +35,7 @@ class OrphanVisitsPaginatorAdapterTest extends TestCase
{
$expectedCount = 5;
$repoCount = $this->repo->countOrphanVisits(
- new VisitsCountFiltering($this->params->getDateRange()),
+ new VisitsCountFiltering($this->params->dateRange),
)->willReturn($expectedCount);
$result = $this->adapter->getNbResults();
@@ -53,7 +53,7 @@ class OrphanVisitsPaginatorAdapterTest extends TestCase
$visitor = Visitor::emptyInstance();
$list = [Visit::forRegularNotFound($visitor), Visit::forInvalidShortUrl($visitor)];
$repoFind = $this->repo->findOrphanVisits(
- new VisitsListFiltering($this->params->getDateRange(), $this->params->excludeBots(), null, $limit, $offset),
+ new VisitsListFiltering($this->params->dateRange, $this->params->excludeBots, null, $limit, $offset),
)->willReturn($list);
$result = $this->adapter->getSlice($offset, $limit);
diff --git a/module/Core/test/Visit/Paginator/Adapter/ShortUrlVisitsPaginatorAdapterTest.php b/module/Core/test/Visit/Paginator/Adapter/ShortUrlVisitsPaginatorAdapterTest.php
index 04e17bc6..ae9a2a64 100644
--- a/module/Core/test/Visit/Paginator/Adapter/ShortUrlVisitsPaginatorAdapterTest.php
+++ b/module/Core/test/Visit/Paginator/Adapter/ShortUrlVisitsPaginatorAdapterTest.php
@@ -68,7 +68,7 @@ class ShortUrlVisitsPaginatorAdapterTest extends TestCase
{
return new ShortUrlVisitsPaginatorAdapter(
$this->repo->reveal(),
- new ShortUrlIdentifier(''),
+ ShortUrlIdentifier::fromShortCodeAndDomain(''),
VisitsParams::fromRawData([]),
$apiKey,
);
diff --git a/module/Core/test/Visit/Transformer/OrphanVisitDataTransformerTest.php b/module/Core/test/Visit/Transformer/OrphanVisitDataTransformerTest.php
index c836cd7c..2d2561bd 100644
--- a/module/Core/test/Visit/Transformer/OrphanVisitDataTransformerTest.php
+++ b/module/Core/test/Visit/Transformer/OrphanVisitDataTransformerTest.php
@@ -10,6 +10,7 @@ use PHPUnit\Framework\TestCase;
use Shlinkio\Shlink\Core\Entity\Visit;
use Shlinkio\Shlink\Core\Entity\VisitLocation;
use Shlinkio\Shlink\Core\Model\Visitor;
+use Shlinkio\Shlink\Core\Visit\Model\VisitType;
use Shlinkio\Shlink\Core\Visit\Transformer\OrphanVisitDataTransformer;
use Shlinkio\Shlink\IpGeolocation\Model\Location;
@@ -44,7 +45,7 @@ class OrphanVisitDataTransformerTest extends TestCase
'visitLocation' => null,
'potentialBot' => false,
'visitedUrl' => '',
- 'type' => Visit::TYPE_BASE_URL,
+ 'type' => VisitType::BASE_URL->value,
],
];
yield 'invalid short url visit' => [
@@ -60,7 +61,7 @@ class OrphanVisitDataTransformerTest extends TestCase
'visitLocation' => null,
'potentialBot' => false,
'visitedUrl' => 'https://example.com/foo',
- 'type' => Visit::TYPE_INVALID_SHORT_URL,
+ 'type' => VisitType::INVALID_SHORT_URL->value,
],
];
yield 'regular 404 visit' => [
@@ -78,7 +79,7 @@ class OrphanVisitDataTransformerTest extends TestCase
'visitLocation' => $location,
'potentialBot' => false,
'visitedUrl' => 'https://doma.in/foo/bar',
- 'type' => Visit::TYPE_REGULAR_404,
+ 'type' => VisitType::REGULAR_404->value,
],
];
}
diff --git a/module/Rest/config/entities-mappings/Shlinkio.Shlink.Rest.Entity.ApiKeyRole.php b/module/Rest/config/entities-mappings/Shlinkio.Shlink.Rest.Entity.ApiKeyRole.php
index b7787b1a..8df324a4 100644
--- a/module/Rest/config/entities-mappings/Shlinkio.Shlink.Rest.Entity.ApiKeyRole.php
+++ b/module/Rest/config/entities-mappings/Shlinkio.Shlink.Rest.Entity.ApiKeyRole.php
@@ -6,7 +6,9 @@ namespace Shlinkio\Shlink\Rest;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping\Builder\ClassMetadataBuilder;
+use Doctrine\ORM\Mapping\Builder\FieldBuilder;
use Doctrine\ORM\Mapping\ClassMetadata;
+use Shlinkio\Shlink\Rest\ApiKey\Role;
use Shlinkio\Shlink\Rest\Entity\ApiKey;
use function Shlinkio\Shlink\Core\determineTableName;
@@ -22,11 +24,14 @@ return static function (ClassMetadata $metadata, array $emConfig): void {
->option('unsigned', true)
->build();
- $builder->createField('roleName', Types::STRING)
- ->columnName('role_name')
- ->length(255)
- ->nullable(false)
- ->build();
+ (new FieldBuilder($builder, [
+ 'fieldName' => 'roleName',
+ 'type' => Types::STRING,
+ 'enumType' => Role::class,
+ ]))->columnName('role_name')
+ ->length(255)
+ ->nullable(false)
+ ->build();
$builder->createField('meta', Types::JSON)
->columnName('meta')
diff --git a/module/Rest/src/Action/AbstractRestAction.php b/module/Rest/src/Action/AbstractRestAction.php
index da8b6d80..f330bab1 100644
--- a/module/Rest/src/Action/AbstractRestAction.php
+++ b/module/Rest/src/Action/AbstractRestAction.php
@@ -8,8 +8,6 @@ use Fig\Http\Message\RequestMethodInterface;
use Fig\Http\Message\StatusCodeInterface;
use Psr\Http\Server\RequestHandlerInterface;
-use function array_merge;
-
abstract class AbstractRestAction implements RequestHandlerInterface, RequestMethodInterface, StatusCodeInterface
{
protected const ROUTE_PATH = '';
@@ -19,7 +17,7 @@ abstract class AbstractRestAction implements RequestHandlerInterface, RequestMet
{
return [
'name' => static::class,
- 'middleware' => array_merge($prevMiddleware, [static::class], $postMiddleware),
+ 'middleware' => [...$prevMiddleware, static::class, ...$postMiddleware],
'path' => static::ROUTE_PATH,
'allowed_methods' => static::ROUTE_ALLOWED_METHODS,
];
diff --git a/module/Rest/src/Action/Tag/ListTagsAction.php b/module/Rest/src/Action/Tag/ListTagsAction.php
index ab81400c..d52436d2 100644
--- a/module/Rest/src/Action/Tag/ListTagsAction.php
+++ b/module/Rest/src/Action/Tag/ListTagsAction.php
@@ -32,7 +32,7 @@ class ListTagsAction extends AbstractRestAction
$params = TagsParams::fromRawData($request->getQueryParams());
$apiKey = AuthenticationMiddleware::apiKeyFromRequest($request);
- if (! $params->withStats()) {
+ if (! $params->withStats) {
return new JsonResponse([
'tags' => $this->serializePaginator($this->tagService->listTags($params, $apiKey)),
]);
@@ -41,7 +41,7 @@ class ListTagsAction extends AbstractRestAction
// This part is deprecated. To get tags with stats, the /tags/stats endpoint should be used instead
$tagsInfo = $this->tagService->tagsInfo($params, $apiKey);
$rawTags = $this->serializePaginator($tagsInfo, null, 'stats');
- $rawTags['data'] = map($tagsInfo, static fn (TagInfo $info) => $info->tag());
+ $rawTags['data'] = map($tagsInfo, static fn (TagInfo $info) => $info->tag);
return new JsonResponse(['tags' => $rawTags]);
}
diff --git a/module/Rest/src/ApiKey/Model/ApiKeyMeta.php b/module/Rest/src/ApiKey/Model/ApiKeyMeta.php
index 39b5dca1..430221a2 100644
--- a/module/Rest/src/ApiKey/Model/ApiKeyMeta.php
+++ b/module/Rest/src/ApiKey/Model/ApiKeyMeta.php
@@ -8,11 +8,13 @@ use Cake\Chronos\Chronos;
final class ApiKeyMeta
{
+ /**
+ * @param RoleDefinition[] $roleDefinitions
+ */
private function __construct(
- private ?string $name,
- private ?Chronos $expirationDate,
- /** @var RoleDefinition[] */
- private array $roleDefinitions,
+ public readonly ?string $name,
+ public readonly ?Chronos $expirationDate,
+ public readonly array $roleDefinitions,
) {
}
@@ -35,22 +37,4 @@ final class ApiKeyMeta
{
return new self(null, null, $roleDefinitions);
}
-
- public function name(): ?string
- {
- return $this->name;
- }
-
- public function expirationDate(): ?Chronos
- {
- return $this->expirationDate;
- }
-
- /**
- * @return RoleDefinition[]
- */
- public function roleDefinitions(): array
- {
- return $this->roleDefinitions;
- }
}
diff --git a/module/Rest/src/ApiKey/Model/RoleDefinition.php b/module/Rest/src/ApiKey/Model/RoleDefinition.php
index fdd4d5cb..63c9b72a 100644
--- a/module/Rest/src/ApiKey/Model/RoleDefinition.php
+++ b/module/Rest/src/ApiKey/Model/RoleDefinition.php
@@ -9,7 +9,7 @@ use Shlinkio\Shlink\Rest\ApiKey\Role;
final class RoleDefinition
{
- private function __construct(private string $roleName, private array $meta)
+ private function __construct(public readonly Role $role, public readonly array $meta)
{
}
@@ -25,14 +25,4 @@ final class RoleDefinition
['domain_id' => $domain->getId(), 'authority' => $domain->getAuthority()],
);
}
-
- public function roleName(): string
- {
- return $this->roleName;
- }
-
- public function meta(): array
- {
- return $this->meta;
- }
}
diff --git a/module/Rest/src/ApiKey/Role.php b/module/Rest/src/ApiKey/Role.php
index 557abd00..64803969 100644
--- a/module/Rest/src/ApiKey/Role.php
+++ b/module/Rest/src/ApiKey/Role.php
@@ -2,6 +2,8 @@
declare(strict_types=1);
+// phpcs:disable
+// TODO Enable coding style checks again once code sniffer 3.7 is released https://github.com/squizlabs/PHP_CodeSniffer/issues/3474
namespace Shlinkio\Shlink\Rest\ApiKey;
use Happyr\DoctrineSpecification\Spec;
@@ -12,30 +14,24 @@ use Shlinkio\Shlink\Core\ShortUrl\Spec\BelongsToDomain;
use Shlinkio\Shlink\Core\ShortUrl\Spec\BelongsToDomainInlined;
use Shlinkio\Shlink\Rest\Entity\ApiKeyRole;
-class Role
+enum Role: string
{
- public const AUTHORED_SHORT_URLS = 'AUTHORED_SHORT_URLS';
- public const DOMAIN_SPECIFIC = 'DOMAIN_SPECIFIC';
- private const ROLE_FRIENDLY_NAMES = [
- self::AUTHORED_SHORT_URLS => 'Author only',
- self::DOMAIN_SPECIFIC => 'Domain only',
- ];
+ case AUTHORED_SHORT_URLS = 'AUTHORED_SHORT_URLS';
+ case DOMAIN_SPECIFIC = 'DOMAIN_SPECIFIC';
public static function toSpec(ApiKeyRole $role, ?string $context = null): Specification
{
- return match ($role->name()) {
+ return match ($role->role()) {
self::AUTHORED_SHORT_URLS => new BelongsToApiKey($role->apiKey(), $context),
self::DOMAIN_SPECIFIC => new BelongsToDomain(self::domainIdFromMeta($role->meta()), $context),
- default => Spec::andX(),
};
}
public static function toInlinedSpec(ApiKeyRole $role): Specification
{
- return match ($role->name()) {
+ return match ($role->role()) {
self::AUTHORED_SHORT_URLS => Spec::andX(new BelongsToApiKeyInlined($role->apiKey())),
self::DOMAIN_SPECIFIC => Spec::andX(new BelongsToDomainInlined(self::domainIdFromMeta($role->meta()))),
- default => Spec::andX(),
};
}
@@ -49,8 +45,11 @@ class Role
return $meta['authority'] ?? '';
}
- public static function toFriendlyName(string $roleName): string
+ public static function toFriendlyName(Role $role): string
{
- return self::ROLE_FRIENDLY_NAMES[$roleName] ?? '';
+ return match ($role) {
+ self::AUTHORED_SHORT_URLS => 'Author only',
+ self::DOMAIN_SPECIFIC => 'Domain only',
+ };
}
}
diff --git a/module/Rest/src/Entity/ApiKey.php b/module/Rest/src/Entity/ApiKey.php
index 2940bc69..261baee4 100644
--- a/module/Rest/src/Entity/ApiKey.php
+++ b/module/Rest/src/Entity/ApiKey.php
@@ -44,8 +44,8 @@ class ApiKey extends AbstractEntity
public static function fromMeta(ApiKeyMeta $meta): self
{
- $apiKey = new self($meta->name(), $meta->expirationDate());
- foreach ($meta->roleDefinitions() as $roleDefinition) {
+ $apiKey = new self($meta->name, $meta->expirationDate);
+ foreach ($meta->roleDefinitions as $roleDefinition) {
$apiKey->registerRole($roleDefinition);
}
@@ -113,45 +113,40 @@ class ApiKey extends AbstractEntity
return $this->roles->isEmpty();
}
- public function hasRole(string $roleName): bool
+ public function hasRole(Role $role): bool
{
- return $this->roles->containsKey($roleName);
+ return $this->roles->containsKey($role->value);
}
- public function getRoleMeta(string $roleName): array
+ public function getRoleMeta(Role $role): array
{
- /** @var ApiKeyRole|null $role */
- $role = $this->roles->get($roleName);
- return $role?->meta() ?? [];
+ /** @var ApiKeyRole|null $apiKeyRole */
+ $apiKeyRole = $this->roles->get($role->value);
+ return $apiKeyRole?->meta() ?? [];
}
/**
* @template T
- * @param callable(string $roleName, array $meta): T $fun
+ * @param callable(Role $role, array $meta): T $fun
* @return T[]
*/
public function mapRoles(callable $fun): array
{
- return $this->roles->map(fn (ApiKeyRole $role) => $fun($role->name(), $role->meta()))->getValues();
+ return $this->roles->map(fn (ApiKeyRole $role) => $fun($role->role(), $role->meta()))->getValues();
}
public function registerRole(RoleDefinition $roleDefinition): void
{
- $roleName = $roleDefinition->roleName();
- $meta = $roleDefinition->meta();
+ $role = $roleDefinition->role;
+ $meta = $roleDefinition->meta;
- if ($this->hasRole($roleName)) {
- /** @var ApiKeyRole $role */
- $role = $this->roles->get($roleName);
- $role->updateMeta($meta);
+ if ($this->hasRole($role)) {
+ /** @var ApiKeyRole $apiKeyRole */
+ $apiKeyRole = $this->roles->get($role);
+ $apiKeyRole->updateMeta($meta);
} else {
- $role = new ApiKeyRole($roleDefinition->roleName(), $roleDefinition->meta(), $this);
- $this->roles[$roleName] = $role;
+ $apiKeyRole = new ApiKeyRole($roleDefinition->role, $roleDefinition->meta, $this);
+ $this->roles[$role->value] = $apiKeyRole;
}
}
-
- public function removeRole(string $roleName): void
- {
- $this->roles->remove($roleName);
- }
}
diff --git a/module/Rest/src/Entity/ApiKeyRole.php b/module/Rest/src/Entity/ApiKeyRole.php
index 1155c37b..8491cfce 100644
--- a/module/Rest/src/Entity/ApiKeyRole.php
+++ b/module/Rest/src/Entity/ApiKeyRole.php
@@ -5,14 +5,15 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\Rest\Entity;
use Shlinkio\Shlink\Common\Entity\AbstractEntity;
+use Shlinkio\Shlink\Rest\ApiKey\Role;
class ApiKeyRole extends AbstractEntity
{
- public function __construct(private string $roleName, private array $meta, private ApiKey $apiKey)
+ public function __construct(private Role $roleName, private array $meta, private ApiKey $apiKey)
{
}
- public function name(): string
+ public function role(): Role
{
return $this->roleName;
}
diff --git a/module/Rest/src/Middleware/AuthenticationMiddleware.php b/module/Rest/src/Middleware/AuthenticationMiddleware.php
index 25f1fbe5..7b911817 100644
--- a/module/Rest/src/Middleware/AuthenticationMiddleware.php
+++ b/module/Rest/src/Middleware/AuthenticationMiddleware.php
@@ -49,7 +49,7 @@ class AuthenticationMiddleware implements MiddlewareInterface, StatusCodeInterfa
throw VerifyAuthenticationException::forInvalidApiKey();
}
- return $handler->handle($request->withAttribute(ApiKey::class, $result->apiKey()));
+ return $handler->handle($request->withAttribute(ApiKey::class, $result->apiKey));
}
public static function apiKeyFromRequest(Request $request): ApiKey
diff --git a/module/Rest/src/Middleware/CrossDomainMiddleware.php b/module/Rest/src/Middleware/CrossDomainMiddleware.php
index b0d63dc7..d6a51a0c 100644
--- a/module/Rest/src/Middleware/CrossDomainMiddleware.php
+++ b/module/Rest/src/Middleware/CrossDomainMiddleware.php
@@ -11,7 +11,6 @@ use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
-use function array_merge;
use function implode;
class CrossDomainMiddleware implements MiddlewareInterface, RequestMethodInterface
@@ -45,7 +44,7 @@ class CrossDomainMiddleware implements MiddlewareInterface, RequestMethodInterfa
];
// Options requests should always be empty and have a 204 status code
- return EmptyResponse::withHeaders(array_merge($response->getHeaders(), $corsHeaders));
+ return EmptyResponse::withHeaders([...$response->getHeaders(), ...$corsHeaders]);
}
private function resolveCorsAllowedMethods(ResponseInterface $response): string
diff --git a/module/Rest/src/Service/ApiKeyCheckResult.php b/module/Rest/src/Service/ApiKeyCheckResult.php
index 2caee4e1..ff74fb79 100644
--- a/module/Rest/src/Service/ApiKeyCheckResult.php
+++ b/module/Rest/src/Service/ApiKeyCheckResult.php
@@ -8,7 +8,7 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey;
final class ApiKeyCheckResult
{
- public function __construct(private ?ApiKey $apiKey = null)
+ public function __construct(public readonly ?ApiKey $apiKey = null)
{
}
@@ -16,9 +16,4 @@ final class ApiKeyCheckResult
{
return $this->apiKey !== null && $this->apiKey->isValid();
}
-
- public function apiKey(): ?ApiKey
- {
- return $this->apiKey;
- }
}
diff --git a/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php b/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php
index 55828368..05212fe7 100644
--- a/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php
+++ b/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php
@@ -44,9 +44,9 @@ class DomainRedirectsRequestTest extends TestCase
$notFound = $request->toNotFoundRedirects($defaults);
self::assertEquals($expectedAuthority, $request->authority());
- self::assertEquals($expectedBaseUrlRedirect, $notFound->baseUrlRedirect());
- self::assertEquals($expectedRegular404Redirect, $notFound->regular404Redirect());
- self::assertEquals($expectedInvalidShortUrlRedirect, $notFound->invalidShortUrlRedirect());
+ self::assertEquals($expectedBaseUrlRedirect, $notFound->baseUrlRedirect);
+ self::assertEquals($expectedRegular404Redirect, $notFound->regular404Redirect);
+ self::assertEquals($expectedInvalidShortUrlRedirect, $notFound->invalidShortUrlRedirect);
}
public function provideValidData(): iterable
diff --git a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php
index 04ffb107..19422d9d 100644
--- a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php
+++ b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php
@@ -36,9 +36,11 @@ class ResolveShortUrlActionTest extends TestCase
{
$shortCode = 'abc123';
$apiKey = ApiKey::create();
- $this->urlResolver->resolveShortUrl(new ShortUrlIdentifier($shortCode), $apiKey)->willReturn(
- ShortUrl::withLongUrl('http://domain.com/foo/bar'),
- )->shouldBeCalledOnce();
+ $this->urlResolver->resolveShortUrl(
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
+ $apiKey,
+ )->willReturn(ShortUrl::withLongUrl('http://domain.com/foo/bar'))
+ ->shouldBeCalledOnce();
$request = (new ServerRequest())->withAttribute('shortCode', $shortCode)->withAttribute(ApiKey::class, $apiKey);
$response = $this->action->handle($request);
diff --git a/module/Rest/test/Action/Visit/ShortUrlVisitsActionTest.php b/module/Rest/test/Action/Visit/ShortUrlVisitsActionTest.php
index 6e982aec..50cabe8e 100644
--- a/module/Rest/test/Action/Visit/ShortUrlVisitsActionTest.php
+++ b/module/Rest/test/Action/Visit/ShortUrlVisitsActionTest.php
@@ -38,7 +38,7 @@ class ShortUrlVisitsActionTest extends TestCase
{
$shortCode = 'abc123';
$this->visitsHelper->visitsForShortUrl(
- new ShortUrlIdentifier($shortCode),
+ ShortUrlIdentifier::fromShortCodeAndDomain($shortCode),
Argument::type(VisitsParams::class),
Argument::type(ApiKey::class),
)->willReturn(new Paginator(new ArrayAdapter([])))
@@ -52,7 +52,7 @@ class ShortUrlVisitsActionTest extends TestCase
public function paramsAreReadFromQuery(): void
{
$shortCode = 'abc123';
- $this->visitsHelper->visitsForShortUrl(new ShortUrlIdentifier($shortCode), new VisitsParams(
+ $this->visitsHelper->visitsForShortUrl(ShortUrlIdentifier::fromShortCodeAndDomain($shortCode), new VisitsParams(
DateRange::withEndDate(Chronos::parse('2016-01-01 00:00:00')),
3,
10,
diff --git a/module/Rest/test/ApiKey/Model/RoleDefinitionTest.php b/module/Rest/test/ApiKey/Model/RoleDefinitionTest.php
index 8e6a58ad..ba27a02f 100644
--- a/module/Rest/test/ApiKey/Model/RoleDefinitionTest.php
+++ b/module/Rest/test/ApiKey/Model/RoleDefinitionTest.php
@@ -16,8 +16,8 @@ class RoleDefinitionTest extends TestCase
{
$definition = RoleDefinition::forAuthoredShortUrls();
- self::assertEquals(Role::AUTHORED_SHORT_URLS, $definition->roleName());
- self::assertEquals([], $definition->meta());
+ self::assertEquals(Role::AUTHORED_SHORT_URLS, $definition->role);
+ self::assertEquals([], $definition->meta);
}
/** @test */
@@ -26,7 +26,7 @@ class RoleDefinitionTest extends TestCase
$domain = Domain::withAuthority('foo.com')->setId('123');
$definition = RoleDefinition::forDomain($domain);
- self::assertEquals(Role::DOMAIN_SPECIFIC, $definition->roleName());
- self::assertEquals(['domain_id' => '123', 'authority' => 'foo.com'], $definition->meta());
+ self::assertEquals(Role::DOMAIN_SPECIFIC, $definition->role);
+ self::assertEquals(['domain_id' => '123', 'authority' => 'foo.com'], $definition->meta);
}
}
diff --git a/module/Rest/test/ApiKey/RoleTest.php b/module/Rest/test/ApiKey/RoleTest.php
index 7ee23076..f3cc64b2 100644
--- a/module/Rest/test/ApiKey/RoleTest.php
+++ b/module/Rest/test/ApiKey/RoleTest.php
@@ -30,7 +30,6 @@ class RoleTest extends TestCase
{
$apiKey = ApiKey::create();
- yield 'invalid role' => [new ApiKeyRole('invalid', [], $apiKey), Spec::andX()];
yield 'author role' => [
new ApiKeyRole(Role::AUTHORED_SHORT_URLS, [], $apiKey),
new BelongsToApiKey($apiKey),
@@ -54,7 +53,6 @@ class RoleTest extends TestCase
{
$apiKey = ApiKey::create();
- yield 'invalid role' => [new ApiKeyRole('invalid', [], $apiKey), Spec::andX()];
yield 'author role' => [
new ApiKeyRole(Role::AUTHORED_SHORT_URLS, [], $apiKey),
Spec::andX(new BelongsToApiKeyInlined($apiKey)),
@@ -101,15 +99,14 @@ class RoleTest extends TestCase
* @test
* @dataProvider provideRoleNames
*/
- public function getsExpectedRoleFriendlyName(string $roleName, string $expectedFriendlyName): void
+ public function getsExpectedRoleFriendlyName(Role $roleName, string $expectedFriendlyName): void
{
self::assertEquals($expectedFriendlyName, Role::toFriendlyName($roleName));
}
public function provideRoleNames(): iterable
{
- yield 'unknown' => ['unknown', ''];
- yield Role::AUTHORED_SHORT_URLS => [Role::AUTHORED_SHORT_URLS, 'Author only'];
- yield Role::DOMAIN_SPECIFIC => [Role::DOMAIN_SPECIFIC, 'Domain only'];
+ yield Role::AUTHORED_SHORT_URLS->value => [Role::AUTHORED_SHORT_URLS, 'Author only'];
+ yield Role::DOMAIN_SPECIFIC->value => [Role::DOMAIN_SPECIFIC, 'Domain only'];
}
}
diff --git a/module/Rest/test/Service/ApiKeyServiceTest.php b/module/Rest/test/Service/ApiKeyServiceTest.php
index de17d8bd..aba79036 100644
--- a/module/Rest/test/Service/ApiKeyServiceTest.php
+++ b/module/Rest/test/Service/ApiKeyServiceTest.php
@@ -46,7 +46,7 @@ class ApiKeyServiceTest extends TestCase
self::assertEquals($date, $key->getExpirationDate());
self::assertEquals($name, $key->name());
foreach ($roles as $roleDefinition) {
- self::assertTrue($key->hasRole($roleDefinition->roleName()));
+ self::assertTrue($key->hasRole($roleDefinition->role));
}
}
@@ -77,7 +77,7 @@ class ApiKeyServiceTest extends TestCase
$result = $this->service->check('12345');
self::assertFalse($result->isValid());
- self::assertSame($invalidKey, $result->apiKey());
+ self::assertSame($invalidKey, $result->apiKey);
}
public function provideInvalidApiKeys(): iterable
@@ -100,7 +100,7 @@ class ApiKeyServiceTest extends TestCase
$result = $this->service->check('12345');
self::assertTrue($result->isValid());
- self::assertSame($apiKey, $result->apiKey());
+ self::assertSame($apiKey, $result->apiKey);
}
/** @test */