2023-11-15 19:57:58 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace Shlinkio\Shlink\Core\EventDispatcher\Matomo;
|
|
|
|
|
|
|
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
|
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
|
use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated;
|
|
|
|
|
use Shlinkio\Shlink\Core\Matomo\MatomoOptions;
|
|
|
|
|
use Shlinkio\Shlink\Core\Matomo\MatomoTrackerBuilderInterface;
|
|
|
|
|
use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier;
|
|
|
|
|
use Shlinkio\Shlink\Core\Visit\Entity\Visit;
|
|
|
|
|
use Throwable;
|
|
|
|
|
|
2024-03-31 13:51:03 +02:00
|
|
|
readonly class SendVisitToMatomo
|
2023-11-15 19:57:58 +01:00
|
|
|
{
|
|
|
|
|
public function __construct(
|
2024-03-31 13:51:03 +02:00
|
|
|
private EntityManagerInterface $em,
|
|
|
|
|
private LoggerInterface $logger,
|
|
|
|
|
private ShortUrlStringifier $shortUrlStringifier,
|
|
|
|
|
private MatomoOptions $matomoOptions,
|
|
|
|
|
private MatomoTrackerBuilderInterface $trackerBuilder,
|
2023-11-15 19:57:58 +01:00
|
|
|
) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function __invoke(VisitLocated $visitLocated): void
|
|
|
|
|
{
|
|
|
|
|
if (! $this->matomoOptions->enabled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$visitId = $visitLocated->visitId;
|
|
|
|
|
|
|
|
|
|
/** @var Visit|null $visit */
|
|
|
|
|
$visit = $this->em->find(Visit::class, $visitId);
|
|
|
|
|
if ($visit === null) {
|
|
|
|
|
$this->logger->warning('Tried to send visit with id "{visitId}" to matomo, but it does not exist.', [
|
|
|
|
|
'visitId' => $visitId,
|
|
|
|
|
]);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$tracker = $this->trackerBuilder->buildMatomoTracker();
|
|
|
|
|
|
|
|
|
|
$tracker
|
|
|
|
|
->setUrl($this->resolveUrlToTrack($visit))
|
2024-03-18 19:57:30 +01:00
|
|
|
->setCustomTrackingParameter('type', $visit->type->value)
|
|
|
|
|
->setUserAgent($visit->userAgent)
|
|
|
|
|
->setUrlReferrer($visit->referer);
|
2023-11-15 19:57:58 +01:00
|
|
|
|
|
|
|
|
$location = $visit->getVisitLocation();
|
|
|
|
|
if ($location !== null) {
|
|
|
|
|
$tracker
|
2024-03-18 19:11:42 +01:00
|
|
|
->setCity($location->cityName)
|
|
|
|
|
->setCountry($location->countryName)
|
|
|
|
|
->setLatitude($location->latitude)
|
|
|
|
|
->setLongitude($location->longitude);
|
2023-11-15 19:57:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set not obfuscated IP if possible, as matomo handles obfuscation itself
|
2024-03-18 19:57:30 +01:00
|
|
|
$ip = $visitLocated->originalIpAddress ?? $visit->remoteAddr;
|
2023-11-15 19:57:58 +01:00
|
|
|
if ($ip !== null) {
|
|
|
|
|
$tracker->setIp($ip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($visit->isOrphan()) {
|
|
|
|
|
$tracker->setCustomTrackingParameter('orphan', 'true');
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-31 13:51:03 +02:00
|
|
|
// Send the short URL title or an empty document title to avoid different actions to be created by matomo
|
|
|
|
|
$tracker->doTrackPageView($visit->shortUrl?->title() ?? '');
|
2023-11-15 19:57:58 +01:00
|
|
|
} catch (Throwable $e) {
|
|
|
|
|
// Capture all exceptions to make sure this does not interfere with the regular execution
|
|
|
|
|
$this->logger->error('An error occurred while trying to send visit to Matomo. {e}', ['e' => $e]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function resolveUrlToTrack(Visit $visit): string
|
|
|
|
|
{
|
2024-03-18 19:57:30 +01:00
|
|
|
$shortUrl = $visit->shortUrl;
|
2023-11-15 19:57:58 +01:00
|
|
|
if ($shortUrl === null) {
|
2024-03-18 19:57:30 +01:00
|
|
|
return $visit->visitedUrl ?? '';
|
2023-11-15 19:57:58 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->shortUrlStringifier->stringify($shortUrl);
|
|
|
|
|
}
|
|
|
|
|
}
|