First commit
This commit is contained in:
14 files changed
+427
-140
No files matched your search
@@ -1,19 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class MatomoProxyLaravelCommand extends Command
|
||||
{
|
||||
public $signature = 'matomo-proxy-laravel';
|
||||
|
||||
public $description = 'My command';
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$this->comment('All done');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
/**
|
||||
* @see \MattitjaAB\MatomoProxyLaravel\MatomoProxyLaravel
|
||||
*/
|
||||
class MatomoProxyLaravel extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor(): string
|
||||
{
|
||||
return \MattitjaAB\MatomoProxyLaravel\MatomoProxyLaravel::class;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel;
|
||||
|
||||
class MatomoProxyLaravel {}
|
||||
@@ -2,24 +2,23 @@
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel;
|
||||
|
||||
use MattitjaAB\MatomoProxyLaravel\Commands\MatomoProxyLaravelCommand;
|
||||
use Illuminate\Contracts\Http\Kernel as HttpKernel;
|
||||
use MattitjaAB\MatomoProxyLaravel\Middleware\MatomoProxyLaravelMiddleware;
|
||||
use Spatie\LaravelPackageTools\Package;
|
||||
use Spatie\LaravelPackageTools\PackageServiceProvider;
|
||||
|
||||
class MatomoProxyLaravelServiceProvider extends PackageServiceProvider
|
||||
final class MatomoProxyLaravelServiceProvider extends PackageServiceProvider
|
||||
{
|
||||
public function configurePackage(Package $package): void
|
||||
{
|
||||
/*
|
||||
* This class is a Package Service Provider
|
||||
*
|
||||
* More info: https://github.com/spatie/laravel-package-tools
|
||||
*/
|
||||
$package
|
||||
->name('matomo-proxy-laravel')
|
||||
->hasConfigFile()
|
||||
->hasViews()
|
||||
->hasMigration('create_matomo_proxy_laravel_table')
|
||||
->hasCommand(MatomoProxyLaravelCommand::class);
|
||||
->hasConfigFile('matomo-proxy-laravel');
|
||||
}
|
||||
|
||||
public function packageBooted(): void
|
||||
{
|
||||
$this->app->make(HttpKernel::class)
|
||||
->pushMiddleware(MatomoProxyLaravelMiddleware::class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MattitjaAB\MatomoProxyLaravel\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
final class MatomoProxyLaravelMiddleware
|
||||
{
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
/** @var Response $response */
|
||||
$response = $next($request);
|
||||
|
||||
// Only track GET requests
|
||||
if (! $request->isMethod('GET')) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Global feature toggle + required config
|
||||
if (! $this->isEnabled()) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Only track HTML responses
|
||||
if (! $this->isHtmlResponse($response)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Respect ignore patterns
|
||||
if ($this->isIgnoredPath($request)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Skip non-success/redirect responses (e.g., 404, 5xx)
|
||||
if (! $this->isTrackableStatus($response)) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Ensure endpoint is present
|
||||
$endpoint = $this->buildEndpoint();
|
||||
if ($endpoint === null) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Build payload now; do not capture Request in any deferred closure
|
||||
$params = $this->buildParams($request);
|
||||
|
||||
// Send synchronously (you can swap to app()->terminating() if desired)
|
||||
$this->send($endpoint, $params);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enabled flag and required keys.
|
||||
*/
|
||||
private function isEnabled(): bool
|
||||
{
|
||||
return config('matomo-proxy-laravel.enabled') === true
|
||||
&& filled(config('matomo-proxy-laravel.base_url'))
|
||||
&& filled(config('matomo-proxy-laravel.site_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Only track HTML (also accept application/xhtml+xml).
|
||||
*/
|
||||
private function isHtmlResponse(Response $response): bool
|
||||
{
|
||||
$contentType = $response->headers->get('content-type');
|
||||
|
||||
return $contentType !== null
|
||||
&& (str_starts_with($contentType, 'text/html')
|
||||
|| str_starts_with($contentType, 'application/xhtml+xml'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Respect configurable ignore patterns.
|
||||
*/
|
||||
private function isIgnoredPath(Request $request): bool
|
||||
{
|
||||
foreach (config('matomo-proxy-laravel.ignore', []) as $pattern) {
|
||||
if ($request->is($pattern)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count only successful (2xx) and redirect (3xx) responses.
|
||||
*/
|
||||
private function isTrackableStatus(Response $response): bool
|
||||
{
|
||||
$code = $response->getStatusCode();
|
||||
|
||||
return $code >= 200 && $code < 400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint must already include /matomo.php.
|
||||
*/
|
||||
private function buildEndpoint(): ?string
|
||||
{
|
||||
$endpoint = config('matomo-proxy-laravel.base_url');
|
||||
|
||||
return filled($endpoint) ? trim((string) $endpoint) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Matomo Tracking API payload.
|
||||
*/
|
||||
private function buildParams(Request $request): array
|
||||
{
|
||||
$params = [
|
||||
'idsite' => (string) config('matomo-proxy-laravel.site_id', ''),
|
||||
'rec' => 1,
|
||||
'apiv' => 1,
|
||||
'url' => $request->fullUrl(),
|
||||
'action_name' => $request->route()?->getName() ?: $request->path(),
|
||||
'ua' => $request->userAgent(),
|
||||
];
|
||||
|
||||
if ($ref = $request->headers->get('referer')) {
|
||||
$params['urlref'] = $ref;
|
||||
}
|
||||
|
||||
if ($lang = $request->headers->get('accept-language')) {
|
||||
$params['lang'] = $lang;
|
||||
}
|
||||
|
||||
if ($ip = $request->ip()) {
|
||||
$params['cip'] = $ip;
|
||||
}
|
||||
|
||||
if ($token = trim((string) config('matomo-proxy-laravel.token', ''))) {
|
||||
$params['token_auth'] = $token;
|
||||
}
|
||||
|
||||
if ($uid = $request->user()->id ?? null) {
|
||||
$params['uid'] = (string) $uid;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the HTTP request (synchronous).
|
||||
*/
|
||||
private function send(string $endpoint, array $params): void
|
||||
{
|
||||
$timeout = (int) config('matomo-proxy-laravel.timeout', 2);
|
||||
$retryTimes = (int) config('matomo-proxy-laravel.retry.times', 0);
|
||||
$retrySleep = (int) config('matomo-proxy-laravel.retry.sleep.ms', 100);
|
||||
|
||||
try {
|
||||
Http::asForm()
|
||||
->timeout($timeout)
|
||||
->retry($retryTimes, $retrySleep)
|
||||
->post($endpoint, $params);
|
||||
} catch (\Throwable) {
|
||||
// Never let analytics affect the response lifecycle
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user