Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 139
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
StravaActivityParser
0.00% covered (danger)
0.00%
0 / 139
0.00% covered (danger)
0.00%
0 / 4
930
0.00% covered (danger)
0.00%
0 / 1
 parse_strava_activity_html
0.00% covered (danger)
0.00%
0 / 46
0.00% covered (danger)
0.00%
0 / 1
90
 parseDateEn1
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
56
 parseDateEn2
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
56
 parseDateDe
0.00% covered (danger)
0.00%
0 / 31
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2
3namespace Olz\Parsers;
4
5use Olz\Utils\WithUtilsTrait;
6
7class StravaActivityParser {
8    use WithUtilsTrait;
9
10    /**
11     * @return array{
12     *   name: ?string,
13     *   sportType: ?string,
14     *   runAt: ?\DateTime,
15     *   distanceMeters: ?int,
16     *   elevationMeters: ?int,
17     * } */
18    public function parse_strava_activity_html(string $html_content): array {
19        $content = str_replace("\n", " ", $html_content);
20        $res = preg_match(
21            "/<span class=\"title\">\\s*<a[^>]*>([^<]+)<\\/a>\\s*[–-]+\\s*([^<]+)<\\/span>/",
22            $content,
23            $matches
24        );
25        $name = $res ? trim($matches[1]) : null;
26        $sport_type = $res ? trim($matches[2]) : null;
27
28        $res = preg_match(
29            "/<div class=\"details\">\\s*<time>([^<]+)<\\/time>/",
30            $content,
31            $matches
32        );
33        $date_string = $res ? trim($matches[1]) : null;
34        $date = $date_string ? ($this->parseDateDe($date_string) ?? $this->parseDateEn1($date_string) ?? $this->parseDateEn2($date_string)) : null;
35
36        $res = preg_match(
37            "/<strong>([0-9\\.\\,]+)<abbr[^>]*>\\s*km<\\/abbr><\\/strong>\\s*<div class=\"label\">(?:Distanz|Distance)<\\/div>/",
38            $content,
39            $matches
40        );
41        $distance1 = $res ? intval(floatval(str_replace(',', '.', trim($matches[1]))) * 1000) : null;
42
43        $res = preg_match(
44            "/<div[^>]*>(?:Distanz|Distance)<\\/div>\\s*<div[^>]*>\\s*<strong>([0-9\\.\\,]+)<abbr[^>]*>\\s*km<\\/abbr><\\/strong>/",
45            $content,
46            $matches
47        );
48        $distance2 = $res ? intval(floatval(str_replace(',', '.', trim($matches[1]))) * 1000) : null;
49
50        $res = preg_match(
51            "/<strong>([0-9\\.\\,]+)<abbr[^>]*>\\s*m<\\/abbr><\\/strong>\\s*<div class=\"label\">(?:Höhenmeter|Elevation)<\\/div>/",
52            $content,
53            $matches
54        );
55        $elevation1 = $res ? intval(str_replace(',', '.', trim($matches[1]))) : null;
56
57        $res = preg_match(
58            "/<div[^>]*>(?:Höhenmeter|Elevation)<\\/div>\\s*<div[^>]*>\\s*<strong>([0-9\\.\\,]+)<abbr[^>]*>\\s*m<\\/abbr><\\/strong>/",
59            $content,
60            $matches
61        );
62        $elevation2 = $res ? intval(str_replace(',', '.', trim($matches[1]))) : null;
63
64        return [
65            'name' => $name,
66            'sportType' => $sport_type,
67            'runAt' => $date,
68            'distanceMeters' => $distance1 ?? $distance2,
69            'elevationMeters' => $elevation1 ?? $elevation2,
70        ];
71    }
72
73    protected function parseDateEn1(string $date_string): ?\DateTime {
74        $pattern = '/([0-9]+):([0-9]+)(?:\s*(AM|PM))? on \w+, ([0-9]+) (\w+),? ([0-9]+)/';
75        $res = preg_match($pattern, $date_string, $matches);
76        if (!$res) {
77            return null;
78        }
79        $hour = intval($matches[1]);
80        $minute = $matches[2];
81        if ($matches[3] === 'PM' && $hour !== 12) {
82            $hour += 12;
83        } elseif ($matches[3] === 'AM' && $hour === 12) {
84            $hour = 0;
85        }
86        $day = $matches[4];
87        $pretty_month = strtolower($matches[5]);
88        $month_map = [
89            'january' => '01',
90            'february' => '02',
91            'march' => '03',
92            'april' => '04',
93            'may' => '05',
94            'june' => '06',
95            'july' => '07',
96            'august' => '08',
97            'september' => '09',
98            'october' => '10',
99            'november' => '11',
100            'december' => '12',
101        ];
102        $month = $month_map[$pretty_month] ?? null;
103        $year = $matches[6];
104        try {
105            return new \DateTime("{$year}-{$month}-{$day} {$hour}:{$minute}:00");
106        } catch (\Throwable $th) {
107            return null;
108        }
109    }
110
111    protected function parseDateEn2(string $date_string): ?\DateTime {
112        $pattern = '/([0-9]+):([0-9]+)(?:\s*(AM|PM))? on \w+, (\w+) ([0-9]+),? ([0-9]+)/';
113        $res = preg_match($pattern, $date_string, $matches);
114        if (!$res) {
115            return null;
116        }
117        $hour = intval($matches[1]);
118        $minute = $matches[2];
119        if ($matches[3] === 'PM' && $hour !== 12) {
120            $hour += 12;
121        } elseif ($matches[3] === 'AM' && $hour === 12) {
122            $hour = 0;
123        }
124        $pretty_month = strtolower($matches[4]);
125        $day = $matches[5];
126        $month_map = [
127            'january' => '01',
128            'february' => '02',
129            'march' => '03',
130            'april' => '04',
131            'may' => '05',
132            'june' => '06',
133            'july' => '07',
134            'august' => '08',
135            'september' => '09',
136            'october' => '10',
137            'november' => '11',
138            'december' => '12',
139        ];
140        $month = $month_map[$pretty_month] ?? null;
141        $year = $matches[6];
142        try {
143            return new \DateTime("{$year}-{$month}-{$day} {$hour}:{$minute}:00");
144        } catch (\Throwable $th) {
145            return null;
146        }
147    }
148
149    protected function parseDateDe(string $date_string): ?\DateTime {
150        $pattern = '/([0-9]+):([0-9]+)(?:\s*(AM|PM))? am \w+, den ([0-9]+). (\w+),? ([0-9]+)/';
151        $res = preg_match($pattern, $date_string, $matches);
152        if (!$res) {
153            return null;
154        }
155        $hour = intval($matches[1]);
156        $minute = $matches[2];
157        if ($matches[3] === 'PM' && $hour !== 12) {
158            $hour += 12;
159        } elseif ($matches[3] === 'AM' && $hour === 12) {
160            $hour = 0;
161        }
162        $day = $matches[4];
163        $pretty_month = strtolower($matches[5]);
164        $month_map = [
165            'januar' => '01',
166            'februar' => '02',
167            'märz' => '03',
168            'april' => '04',
169            'mai' => '05',
170            'juni' => '06',
171            'juli' => '07',
172            'august' => '08',
173            'september' => '09',
174            'oktober' => '10',
175            'november' => '11',
176            'dezember' => '12',
177        ];
178        $month = $month_map[$pretty_month] ?? null;
179        $year = $matches[6];
180        try {
181            return new \DateTime("{$year}-{$month}-{$day} {$hour}:{$minute}:00");
182        } catch (\Throwable $th) {
183            return null;
184        }
185    }
186}