Skip to content

Commit 36ddb00

Browse files
authored
Merge pull request #16 from Astylodon/extend-visit
Add more info to visits
2 parents 087815a + 5539859 commit 36ddb00

File tree

10 files changed

+319
-4
lines changed

10 files changed

+319
-4
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
DB_DSN=mysql:host=localhost;dbname=shika
22
DB_USERNAME=
33
DB_PASSWORD=
4+
5+
MAXMIND_PATH=

bootstrap/dependencies.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use DI\ContainerBuilder;
44
use Dotenv\Dotenv;
55
use Shika\Database\Database;
6+
use Shika\Helpers\GeoLocation;
67
use Shika\Helpers\Session;
78
use Shika\Repositories\ApiKeyRepository;
89
use Shika\Repositories\SiteRepository;
@@ -31,6 +32,8 @@
3132

3233
Twig::class => autowire(Twig::class),
3334
Session::class => autowire(Session::class),
35+
36+
GeoLocation::class => autowire(GeoLocation::class)->constructor(env("MAXMIND_PATH", "")),
3437
]
3538
);
3639
};

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
"twig/twig": "^3.0",
1010
"maxmind-db/reader": "^1.11",
1111
"astylodon/migrations": "^1.0",
12-
"vlucas/phpdotenv": "^5.6"
12+
"vlucas/phpdotenv": "^5.6",
13+
"matomo/device-detector": "^6.4"
1314
},
1415
"autoload": {
1516
"psr-4": {

composer.lock

Lines changed: 124 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
use Astylodon\Migrations\Database\DatabaseInterface;
4+
use Astylodon\Migrations\Migration;
5+
6+
return new class implements Migration
7+
{
8+
public function up(DatabaseInterface $database)
9+
{
10+
$database->exec("ALTER TABLE visits ADD country_code VARCHAR(2)");
11+
$database->exec("ALTER TABLE visits ADD browser VARCHAR(50)");
12+
$database->exec("ALTER TABLE visits ADD operating_system VARCHAR(50)");
13+
$database->exec("ALTER TABLE visits ADD device_type VARCHAR(8)");
14+
}
15+
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
use Astylodon\Migrations\Database\DatabaseInterface;
4+
use Astylodon\Migrations\Migration;
5+
6+
return new class implements Migration
7+
{
8+
public function up(DatabaseInterface $database)
9+
{
10+
$database->exec("ALTER TABLE visits ADD country_code TEXT");
11+
$database->exec("ALTER TABLE visits ADD browser TEXT");
12+
$database->exec("ALTER TABLE visits ADD operating_system TEXT");
13+
$database->exec("ALTER TABLE visits ADD device_type TEXT");
14+
}
15+
};

src/Controllers/AnalyticsController.php

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use Psr\Http\Message\ResponseInterface as Response;
66
use Psr\Http\Message\ServerRequestInterface as Request;
7+
use Shika\Helpers\DeviceInfo;
8+
use Shika\Helpers\GeoLocation;
79
use Shika\Helpers\JsonResponse;
810
use Shika\Repositories\SiteRepository;
911
use Shika\Repositories\VisitRepository;
@@ -14,11 +16,13 @@ class AnalyticsController
1416

1517
private VisitRepository $visits;
1618
private SiteRepository $sites;
19+
private GeoLocation $location;
1720

18-
public function __construct(VisitRepository $visits, SiteRepository $sites)
21+
public function __construct(VisitRepository $visits, SiteRepository $sites, GeoLocation $location)
1922
{
2023
$this->visits = $visits;
2124
$this->sites = $sites;
25+
$this->location = $location;
2226
}
2327

2428
public function send(Request $request, Response $response)
@@ -62,13 +66,25 @@ public function send(Request $request, Response $response)
6266
return $response->withStatus(204);
6367
}
6468

69+
// get the agent info
70+
$device = new DeviceInfo($request);
71+
72+
if (!$device->isBrowser())
73+
{
74+
return $response->withStatus(204);
75+
}
76+
6577
// build the visit
6678
$visit = [
6779
"site_id" => $site->id,
6880
"visit_at" => gmdate("Y-m-d H:i:s"),
6981

7082
"visit_host" => $location["host"],
7183
"visit_path" => $location["path"],
84+
85+
"browser" => $device->getBrowser(),
86+
"operating_system" => $device->getOperatingSystem(),
87+
"device_type" => $device->getDeviceType(),
7288
];
7389

7490
// add the referrer if we have one
@@ -82,6 +98,18 @@ public function send(Request $request, Response $response)
8298
}
8399
}
84100

101+
// add location info
102+
if ($this->location->isAvailable())
103+
{
104+
$address = $request->getServerParams()["REMOTE_ADDR"];
105+
$country = $this->location->getCountry($address);
106+
107+
if ($country !== null)
108+
{
109+
$visit["country_code"] = $country["iso_code"];
110+
}
111+
}
112+
85113
// insert the visit
86114
$this->visits->addVisit($visit);
87115

src/Helpers/DeviceInfo.php

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
namespace Shika\Helpers;
4+
5+
use DeviceDetector\DeviceDetector;
6+
use Psr\Http\Message\RequestInterface;
7+
8+
/**
9+
* Helper for getting information about the agent/device
10+
*/
11+
class DeviceInfo
12+
{
13+
private DeviceDetector $detector;
14+
15+
/**
16+
* Constructs a new device info from a request
17+
*/
18+
public function __construct(RequestInterface $request)
19+
{
20+
$userAgent = $request->getHeaderLine("User-Agent");
21+
22+
$detector = new DeviceDetector($userAgent);
23+
$detector->parse();
24+
25+
$this->detector = $detector;
26+
}
27+
28+
/**
29+
* Gets the device operating system
30+
*/
31+
public function getOperatingSystem(): string
32+
{
33+
return $this->detector->getOs("name");
34+
}
35+
36+
/**
37+
* Gets the browser name
38+
*/
39+
public function getBrowser(): string
40+
{
41+
$browser = $this->detector->getClient("name");
42+
43+
switch ($browser)
44+
{
45+
case "Chrome Mobile":
46+
case "Chrome Mobile iOS":
47+
return "Chrome";
48+
49+
case "Firefox Mobile":
50+
case "Firefox Mobile iOS":
51+
return "Firefox";
52+
53+
case "Mobile Safari":
54+
return "Safari";
55+
56+
case "Opera Mobile":
57+
return "Opera";
58+
59+
case "Opera Mini iOS":
60+
return "Opera Mini";
61+
62+
case "Huawei Browser Mobile":
63+
return "Huawei Browser";
64+
65+
default:
66+
return $browser;
67+
}
68+
}
69+
70+
/**
71+
* Gets the device type (phone, tablet, desktop)
72+
*/
73+
public function getDeviceType(): string
74+
{
75+
return ($this->detector->isTablet() ? "tablet" : ($this->detector->isMobile() ? "phone" : "desktop"));
76+
}
77+
78+
/**
79+
* Gets whether the agent is a browser
80+
*/
81+
public function isBrowser(): bool
82+
{
83+
return $this->detector->isBrowser();
84+
}
85+
}

0 commit comments

Comments
 (0)