Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.23% covered (success)
93.23%
124 / 133
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
StravaActivityParser
93.23% covered (success)
93.23%
124 / 133
25.00% covered (danger)
25.00%
1 / 4
21.14
0.00% covered (danger)
0.00%
0 / 1
 parse_strava_activity_html
100.00% covered (success)
100.00%
46 / 46
100.00% covered (success)
100.00%
1 / 1
9
 parseDateEn1
89.66% covered (warning)
89.66%
26 / 29
0.00% covered (danger)
0.00%
0 / 1
4.02
 parseDateEn2
89.66% covered (warning)
89.66%
26 / 29
0.00% covered (danger)
0.00%
0 / 1
4.02
 parseDateDe
89.66% covered (warning)
89.66%
26 / 29
0.00% covered (danger)
0.00%
0 / 1
4.02
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 = $matches[1];
80        $minute = $matches[2];
81        if ($matches[3] === 'PM') {
82            $hour += 12;
83        }
84        $day = $matches[4];
85        $pretty_month = strtolower($matches[5]);
86        $month_map = [
87            'january' => '01',
88            'february' => '02',
89            'march' => '03',
90            'april' => '04',
91            'may' => '05',
92            'june' => '06',
93            'july' => '07',
94            'august' => '08',
95            'september' => '09',
96            'october' => '10',
97            'november' => '11',
98            'december' => '12',
99        ];
100        $month = $month_map[$pretty_month] ?? null;
101        $year = $matches[6];
102        try {
103            return new \DateTime("{$year}-{$month}-{$day} {$hour}:{$minute}:00");
104        } catch (\Throwable $th) {
105            return null;
106        }
107    }
108
109    protected function parseDateEn2(string $date_string): ?\DateTime {
110        $pattern = '/([0-9]+):([0-9]+)(?:\s*(AM|PM))? on \w+, (\w+) ([0-9]+),? ([0-9]+)/';
111        $res = preg_match($pattern, $date_string, $matches);
112        if (!$res) {
113            return null;
114        }
115        $hour = $matches[1];
116        $minute = $matches[2];
117        if ($matches[3] === 'PM') {
118            $hour += 12;
119        }
120        $pretty_month = strtolower($matches[4]);
121        $day = $matches[5];
122        $month_map = [
123            'january' => '01',
124            'february' => '02',
125            'march' => '03',
126            'april' => '04',
127            'may' => '05',
128            'june' => '06',
129            'july' => '07',
130            'august' => '08',
131            'september' => '09',
132            'october' => '10',
133            'november' => '11',
134            'december' => '12',
135        ];
136        $month = $month_map[$pretty_month] ?? null;
137        $year = $matches[6];
138        try {
139            return new \DateTime("{$year}-{$month}-{$day} {$hour}:{$minute}:00");
140        } catch (\Throwable $th) {
141            return null;
142        }
143    }
144
145    protected function parseDateDe(string $date_string): ?\DateTime {
146        $pattern = '/([0-9]+):([0-9]+)(?:\s*(AM|PM))? am \w+, den ([0-9]+). (\w+),? ([0-9]+)/';
147        $res = preg_match($pattern, $date_string, $matches);
148        if (!$res) {
149            return null;
150        }
151        $hour = $matches[1];
152        $minute = $matches[2];
153        if ($matches[3] === 'PM') {
154            $hour += 12;
155        }
156        $day = $matches[4];
157        $pretty_month = strtolower($matches[5]);
158        $month_map = [
159            'januar' => '01',
160            'februar' => '02',
161            'märz' => '03',
162            'april' => '04',
163            'mai' => '05',
164            'juni' => '06',
165            'juli' => '07',
166            'august' => '08',
167            'september' => '09',
168            'oktober' => '10',
169            'november' => '11',
170            'dezember' => '12',
171        ];
172        $month = $month_map[$pretty_month] ?? null;
173        $year = $matches[6];
174        try {
175            return new \DateTime("{$year}-{$month}-{$day} {$hour}:{$minute}:00");
176        } catch (\Throwable $th) {
177            return null;
178        }
179    }
180}