diff --git a/module/CLI/src/Command/Domain/GetDomainVisitsCommand.php b/module/CLI/src/Command/Domain/GetDomainVisitsCommand.php index 8d2eb8c9..dd3797f8 100644 --- a/module/CLI/src/Command/Domain/GetDomainVisitsCommand.php +++ b/module/CLI/src/Command/Domain/GetDomainVisitsCommand.php @@ -44,7 +44,7 @@ class GetDomainVisitsCommand extends AbstractVisitsListCommand */ protected function mapExtraFields(Visit $visit): array { - $shortUrl = $visit->getShortUrl(); + $shortUrl = $visit->shortUrl; return $shortUrl === null ? [] : ['shortUrl' => $this->shortUrlStringifier->stringify($shortUrl)]; } } diff --git a/module/CLI/src/Command/Tag/GetTagVisitsCommand.php b/module/CLI/src/Command/Tag/GetTagVisitsCommand.php index 290a172a..1dfd0ba9 100644 --- a/module/CLI/src/Command/Tag/GetTagVisitsCommand.php +++ b/module/CLI/src/Command/Tag/GetTagVisitsCommand.php @@ -44,7 +44,7 @@ class GetTagVisitsCommand extends AbstractVisitsListCommand */ protected function mapExtraFields(Visit $visit): array { - $shortUrl = $visit->getShortUrl(); + $shortUrl = $visit->shortUrl; return $shortUrl === null ? [] : ['shortUrl' => $this->shortUrlStringifier->stringify($shortUrl)]; } } diff --git a/module/CLI/src/Command/Visit/GetNonOrphanVisitsCommand.php b/module/CLI/src/Command/Visit/GetNonOrphanVisitsCommand.php index 0dd32f3e..1462620d 100644 --- a/module/CLI/src/Command/Visit/GetNonOrphanVisitsCommand.php +++ b/module/CLI/src/Command/Visit/GetNonOrphanVisitsCommand.php @@ -40,7 +40,7 @@ class GetNonOrphanVisitsCommand extends AbstractVisitsListCommand */ protected function mapExtraFields(Visit $visit): array { - $shortUrl = $visit->getShortUrl(); + $shortUrl = $visit->shortUrl; return $shortUrl === null ? [] : ['shortUrl' => $this->shortUrlStringifier->stringify($shortUrl)]; } } diff --git a/module/CLI/src/Command/Visit/GetOrphanVisitsCommand.php b/module/CLI/src/Command/Visit/GetOrphanVisitsCommand.php index 7beae19a..d495db77 100644 --- a/module/CLI/src/Command/Visit/GetOrphanVisitsCommand.php +++ b/module/CLI/src/Command/Visit/GetOrphanVisitsCommand.php @@ -42,6 +42,6 @@ class GetOrphanVisitsCommand extends AbstractVisitsListCommand */ protected function mapExtraFields(Visit $visit): array { - return ['type' => $visit->type()->value]; + return ['type' => $visit->type->value]; } } diff --git a/module/CLI/src/Command/Visit/LocateVisitsCommand.php b/module/CLI/src/Command/Visit/LocateVisitsCommand.php index 9d8d6674..596b287e 100644 --- a/module/CLI/src/Command/Visit/LocateVisitsCommand.php +++ b/module/CLI/src/Command/Visit/LocateVisitsCommand.php @@ -132,7 +132,7 @@ class LocateVisitsCommand extends AbstractLockedCommand implements VisitGeolocat */ public function geolocateVisit(Visit $visit): Location { - $ipAddr = $visit->getRemoteAddr() ?? '?'; + $ipAddr = $visit->remoteAddr ?? '?'; $this->io->write(sprintf('Processing IP %s', $ipAddr)); try { @@ -156,7 +156,7 @@ class LocateVisitsCommand extends AbstractLockedCommand implements VisitGeolocat { if (! $visitLocation->isEmpty) { $this->io->writeln(sprintf(' [Address located in "%s"]', $visitLocation->countryName)); - } elseif ($visit->hasRemoteAddr() && $visit->getRemoteAddr() !== IpAddress::LOCALHOST) { + } elseif ($visit->hasRemoteAddr() && $visit->remoteAddr !== IpAddress::LOCALHOST) { $this->io->writeln(' [Could not locate address]'); } } diff --git a/module/Core/src/EventDispatcher/LocateVisit.php b/module/Core/src/EventDispatcher/LocateVisit.php index f139c0f5..aa6afed8 100644 --- a/module/Core/src/EventDispatcher/LocateVisit.php +++ b/module/Core/src/EventDispatcher/LocateVisit.php @@ -55,7 +55,7 @@ class LocateVisit } $isLocatable = $originalIpAddress !== null || $visit->isLocatable(); - $addr = $originalIpAddress ?? $visit->getRemoteAddr() ?? ''; + $addr = $originalIpAddress ?? $visit->remoteAddr ?? ''; try { $location = $isLocatable ? $this->ipLocationResolver->resolveIpLocation($addr) : Location::emptyInstance(); diff --git a/module/Core/src/EventDispatcher/Matomo/SendVisitToMatomo.php b/module/Core/src/EventDispatcher/Matomo/SendVisitToMatomo.php index ae1514e4..be288fd0 100644 --- a/module/Core/src/EventDispatcher/Matomo/SendVisitToMatomo.php +++ b/module/Core/src/EventDispatcher/Matomo/SendVisitToMatomo.php @@ -46,9 +46,9 @@ class SendVisitToMatomo $tracker ->setUrl($this->resolveUrlToTrack($visit)) - ->setCustomTrackingParameter('type', $visit->type()->value) - ->setUserAgent($visit->userAgent()) - ->setUrlReferrer($visit->referer()); + ->setCustomTrackingParameter('type', $visit->type->value) + ->setUserAgent($visit->userAgent) + ->setUrlReferrer($visit->referer); $location = $visit->getVisitLocation(); if ($location !== null) { @@ -60,7 +60,7 @@ class SendVisitToMatomo } // Set not obfuscated IP if possible, as matomo handles obfuscation itself - $ip = $visitLocated->originalIpAddress ?? $visit->getRemoteAddr(); + $ip = $visitLocated->originalIpAddress ?? $visit->remoteAddr; if ($ip !== null) { $tracker->setIp($ip); } @@ -79,9 +79,9 @@ class SendVisitToMatomo public function resolveUrlToTrack(Visit $visit): string { - $shortUrl = $visit->getShortUrl(); + $shortUrl = $visit->shortUrl; if ($shortUrl === null) { - return $visit->visitedUrl() ?? ''; + return $visit->visitedUrl ?? ''; } return $this->shortUrlStringifier->stringify($shortUrl); diff --git a/module/Core/src/EventDispatcher/PublishingUpdatesGenerator.php b/module/Core/src/EventDispatcher/PublishingUpdatesGenerator.php index 9acdfd04..06d06c84 100644 --- a/module/Core/src/EventDispatcher/PublishingUpdatesGenerator.php +++ b/module/Core/src/EventDispatcher/PublishingUpdatesGenerator.php @@ -20,7 +20,7 @@ final class PublishingUpdatesGenerator implements PublishingUpdatesGeneratorInte public function newVisitUpdate(Visit $visit): Update { return Update::forTopicAndPayload(Topic::NEW_VISIT->value, [ - 'shortUrl' => $this->shortUrlTransformer->transform($visit->getShortUrl()), + 'shortUrl' => $this->shortUrlTransformer->transform($visit->shortUrl), 'visit' => $visit->jsonSerialize(), ]); } @@ -34,7 +34,7 @@ final class PublishingUpdatesGenerator implements PublishingUpdatesGeneratorInte public function newShortUrlVisitUpdate(Visit $visit): Update { - $shortUrl = $visit->getShortUrl(); + $shortUrl = $visit->shortUrl; $topic = Topic::newShortUrlVisit($shortUrl?->getShortCode()); return Update::forTopicAndPayload($topic, [ diff --git a/module/Core/src/Visit/Entity/Visit.php b/module/Core/src/Visit/Entity/Visit.php index 255a55f4..0302f898 100644 --- a/module/Core/src/Visit/Entity/Visit.php +++ b/module/Core/src/Visit/Entity/Visit.php @@ -20,29 +20,22 @@ use function Shlinkio\Shlink\Core\normalizeDate; class Visit extends AbstractEntity implements JsonSerializable { - private string $referer; - private Chronos $date; - private ?string $remoteAddr = null; - private ?string $visitedUrl = null; - private string $userAgent; - private VisitType $type; - private ?ShortUrl $shortUrl; - private ?VisitLocation $visitLocation = null; - private bool $potentialBot; - - private function __construct(?ShortUrl $shortUrl, VisitType $type) - { - $this->shortUrl = $shortUrl; - $this->date = Chronos::now(); - $this->type = $type; + private function __construct( + public readonly ?ShortUrl $shortUrl, + public readonly VisitType $type, + public readonly string $userAgent, + public readonly string $referer, + private readonly bool $potentialBot, + public readonly ?string $remoteAddr = null, + public readonly ?string $visitedUrl = null, + private ?VisitLocation $visitLocation = null, + private Chronos $date = new Chronos(), + ) { } public static function forValidShortUrl(ShortUrl $shortUrl, Visitor $visitor, bool $anonymize = true): self { - $instance = new self($shortUrl, VisitType::VALID_SHORT_URL); - $instance->hydrateFromVisitor($visitor, $anonymize); - - return $instance; + return self::hydrateFromVisitor($shortUrl, VisitType::VALID_SHORT_URL, $visitor, $anonymize); } public static function fromImport(ShortUrl $shortUrl, ImportedShlinkVisit $importedVisit): self @@ -52,13 +45,10 @@ class Visit extends AbstractEntity implements JsonSerializable public static function fromOrphanImport(ImportedShlinkOrphanVisit $importedVisit): self { - $instance = self::fromImportOrOrphanImport( + return self::fromImportOrOrphanImport( $importedVisit, VisitType::tryFrom($importedVisit->type) ?? VisitType::IMPORTED, ); - $instance->visitedUrl = $importedVisit->visitedUrl; - - return $instance; } private static function fromImportOrOrphanImport( @@ -66,52 +56,52 @@ class Visit extends AbstractEntity implements JsonSerializable VisitType $type, ?ShortUrl $shortUrl = null, ): self { - $instance = new self($shortUrl, $type); - $instance->userAgent = $importedVisit->userAgent; - $instance->potentialBot = isCrawler($instance->userAgent); - $instance->referer = $importedVisit->referer; - $instance->date = normalizeDate($importedVisit->date); - $importedLocation = $importedVisit->location; - $instance->visitLocation = $importedLocation !== null ? VisitLocation::fromImport($importedLocation) : null; - - return $instance; + return new self( + shortUrl: $shortUrl, + type: $type, + userAgent: $importedVisit->userAgent, + referer: $importedVisit->referer, + potentialBot: isCrawler($importedVisit->userAgent), + visitedUrl: $importedVisit instanceof ImportedShlinkOrphanVisit ? $importedVisit->visitedUrl : null, + visitLocation: $importedLocation !== null ? VisitLocation::fromImport($importedLocation) : null, + date: normalizeDate($importedVisit->date), + ); } public static function forBasePath(Visitor $visitor, bool $anonymize = true): self { - $instance = new self(null, VisitType::BASE_URL); - $instance->hydrateFromVisitor($visitor, $anonymize); - - return $instance; + return self::hydrateFromVisitor(null, VisitType::BASE_URL, $visitor, $anonymize); } public static function forInvalidShortUrl(Visitor $visitor, bool $anonymize = true): self { - $instance = new self(null, VisitType::INVALID_SHORT_URL); - $instance->hydrateFromVisitor($visitor, $anonymize); - - return $instance; + return self::hydrateFromVisitor(null, VisitType::INVALID_SHORT_URL, $visitor, $anonymize); } public static function forRegularNotFound(Visitor $visitor, bool $anonymize = true): self { - $instance = new self(null, VisitType::REGULAR_404); - $instance->hydrateFromVisitor($visitor, $anonymize); - - return $instance; + return self::hydrateFromVisitor(null, VisitType::REGULAR_404, $visitor, $anonymize); } - private function hydrateFromVisitor(Visitor $visitor, bool $anonymize = true): void - { - $this->userAgent = $visitor->userAgent; - $this->referer = $visitor->referer; - $this->remoteAddr = $this->processAddress($anonymize, $visitor->remoteAddress); - $this->visitedUrl = $visitor->visitedUrl; - $this->potentialBot = $visitor->isPotentialBot(); + private static function hydrateFromVisitor( + ?ShortUrl $shortUrl, + VisitType $type, + Visitor $visitor, + bool $anonymize, + ): self { + return new self( + shortUrl: $shortUrl, + type: $type, + userAgent: $visitor->userAgent, + referer: $visitor->referer, + potentialBot: $visitor->isPotentialBot(), + remoteAddr: self::processAddress($anonymize, $visitor->remoteAddress), + visitedUrl: $visitor->visitedUrl, + ); } - private function processAddress(bool $anonymize, ?string $address): ?string + private static function processAddress(bool $anonymize, ?string $address): ?string { // Localhost addresses do not need to be anonymized if (! $anonymize || $address === null || $address === IpAddress::LOCALHOST) { @@ -125,21 +115,11 @@ class Visit extends AbstractEntity implements JsonSerializable } } - public function getRemoteAddr(): ?string - { - return $this->remoteAddr; - } - public function hasRemoteAddr(): bool { return ! empty($this->remoteAddr); } - public function getShortUrl(): ?ShortUrl - { - return $this->shortUrl; - } - public function getVisitLocation(): ?VisitLocation { return $this->visitLocation; @@ -161,23 +141,13 @@ class Visit extends AbstractEntity implements JsonSerializable return $this->shortUrl === null; } - public function visitedUrl(): ?string - { - return $this->visitedUrl; - } - - public function type(): VisitType - { - return $this->type; - } - /** * Needed only for ArrayCollections to be able to apply criteria filtering * @internal */ public function getType(): VisitType { - return $this->type(); + return $this->type; } /** @@ -188,16 +158,6 @@ class Visit extends AbstractEntity implements JsonSerializable return $this->date; } - public function userAgent(): string - { - return $this->userAgent; - } - - public function referer(): string - { - return $this->referer; - } - public function jsonSerialize(): array { return [ diff --git a/module/Core/src/Visit/Geolocation/VisitToLocationHelper.php b/module/Core/src/Visit/Geolocation/VisitToLocationHelper.php index 2a261019..9d614a7b 100644 --- a/module/Core/src/Visit/Geolocation/VisitToLocationHelper.php +++ b/module/Core/src/Visit/Geolocation/VisitToLocationHelper.php @@ -26,7 +26,7 @@ class VisitToLocationHelper implements VisitToLocationHelperInterface throw IpCannotBeLocatedException::forEmptyAddress(); } - $ipAddr = $visit->getRemoteAddr() ?? ''; + $ipAddr = $visit->remoteAddr ?? ''; if ($ipAddr === IpAddress::LOCALHOST) { throw IpCannotBeLocatedException::forLocalhost(); } diff --git a/module/Core/src/Visit/Transformer/OrphanVisitDataTransformer.php b/module/Core/src/Visit/Transformer/OrphanVisitDataTransformer.php index cf1c8bc9..c4dd6253 100644 --- a/module/Core/src/Visit/Transformer/OrphanVisitDataTransformer.php +++ b/module/Core/src/Visit/Transformer/OrphanVisitDataTransformer.php @@ -15,8 +15,8 @@ class OrphanVisitDataTransformer implements DataTransformerInterface public function transform($visit): array // phpcs:ignore { $serializedVisit = $visit->jsonSerialize(); - $serializedVisit['visitedUrl'] = $visit->visitedUrl(); - $serializedVisit['type'] = $visit->type()->value; + $serializedVisit['visitedUrl'] = $visit->visitedUrl; + $serializedVisit['type'] = $visit->type->value; return $serializedVisit; } diff --git a/module/Core/test/EventDispatcher/LocateVisitTest.php b/module/Core/test/EventDispatcher/LocateVisitTest.php index ddadde84..63595a6c 100644 --- a/module/Core/test/EventDispatcher/LocateVisitTest.php +++ b/module/Core/test/EventDispatcher/LocateVisitTest.php @@ -157,7 +157,7 @@ class LocateVisitTest extends TestCase #[Test, DataProvider('provideIpAddresses')] public function locatableVisitsResolveToLocation(Visit $visit, ?string $originalIpAddress): void { - $ipAddr = $originalIpAddress ?? $visit->getRemoteAddr(); + $ipAddr = $originalIpAddress ?? $visit->remoteAddr; $location = new Location('', '', '', '', 0.0, 0.0, ''); $event = new UrlVisited('123', $originalIpAddress); diff --git a/module/Core/test/EventDispatcher/Matomo/SendVisitToMatomoTest.php b/module/Core/test/EventDispatcher/Matomo/SendVisitToMatomoTest.php index 94c66623..d821bcbb 100644 --- a/module/Core/test/EventDispatcher/Matomo/SendVisitToMatomoTest.php +++ b/module/Core/test/EventDispatcher/Matomo/SendVisitToMatomoTest.php @@ -76,13 +76,13 @@ class SendVisitToMatomoTest extends TestCase if ($visit->isOrphan()) { $tracker->expects($this->exactly(2))->method('setCustomTrackingParameter')->willReturnMap([ - ['type', $visit->type()->value, $tracker], + ['type', $visit->type->value, $tracker], ['orphan', 'true', $tracker], ]); } else { $tracker->expects($this->once())->method('setCustomTrackingParameter')->with( 'type', - $visit->type()->value, + $visit->type->value, )->willReturn($tracker); } diff --git a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php index 545c5b47..75faa25e 100644 --- a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php +++ b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php @@ -93,8 +93,8 @@ class PublishingUpdatesGeneratorTest extends TestCase 'visitLocation' => null, 'date' => $orphanVisit->getDate()->toAtomString(), 'potentialBot' => false, - 'visitedUrl' => $orphanVisit->visitedUrl(), - 'type' => $orphanVisit->type()->value, + 'visitedUrl' => $orphanVisit->visitedUrl, + 'type' => $orphanVisit->type->value, ], ], $update->payload); } diff --git a/module/Core/test/Importer/ImportedLinksProcessorTest.php b/module/Core/test/Importer/ImportedLinksProcessorTest.php index 7c8a17d1..a1816563 100644 --- a/module/Core/test/Importer/ImportedLinksProcessorTest.php +++ b/module/Core/test/Importer/ImportedLinksProcessorTest.php @@ -244,7 +244,7 @@ class ImportedLinksProcessorTest extends TestCase $this->em->expects($this->once())->method('persist')->willReturnCallback( static fn (Visit $visit) => Assert::assertSame( $foundShortUrl ?? $originalShortUrl, - $visit->getShortUrl(), + $visit->shortUrl, ), ); diff --git a/module/Core/test/Visit/Entity/VisitTest.php b/module/Core/test/Visit/Entity/VisitTest.php index 5eb88527..3fea2882 100644 --- a/module/Core/test/Visit/Entity/VisitTest.php +++ b/module/Core/test/Visit/Entity/VisitTest.php @@ -49,7 +49,7 @@ class VisitTest extends TestCase $anonymize, ); - self::assertEquals($expectedAddress, $visit->getRemoteAddr()); + self::assertEquals($expectedAddress, $visit->remoteAddr); } public static function provideAddresses(): iterable