Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
48.57% covered (danger)
48.57%
34 / 70
50.00% covered (danger)
50.00%
3 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
HttpUtils
48.57% covered (danger)
48.57%
34 / 70
50.00% covered (danger)
50.00%
3 / 6
80.99
0.00% covered (danger)
0.00%
0 / 1
 getBotRegexes
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
1
 isBot
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 countRequest
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 dieWithHttpError
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 redirect
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
1
 validateGetParams
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
42
 sendHttpResponseCode
n/a
0 / 0
n/a
0 / 0
1
 sendHeader
n/a
0 / 0
n/a
0 / 0
1
 sendHttpBody
n/a
0 / 0
n/a
0 / 0
1
 exitExecution
n/a
0 / 0
n/a
0 / 0
1
1<?php
2
3namespace Olz\Utils;
4
5use Olz\Components\Error\OlzErrorPage\OlzErrorPage;
6use Olz\Components\Page\OlzFooter\OlzFooter;
7use Olz\Components\Page\OlzHeaderWithoutRouting\OlzHeaderWithoutRouting;
8use Olz\Entity\Counter;
9use PhpTypeScriptApi\PhpStan\PhpStanUtils;
10use PhpTypeScriptApi\PhpStan\ValidateVisitor;
11use Symfony\Component\HttpFoundation\Request;
12
13class HttpUtils {
14    use WithUtilsTrait;
15
16    /** @return array<string> */
17    public function getBotRegexes(): array {
18        return [
19            '/bingbot/i',
20            '/googlebot/i',
21            '/google/i',
22            '/facebookexternalhit/i',
23            '/applebot/i',
24            '/yandexbot/i',
25            '/ecosia/i',
26            '/phpservermon/i',
27            '/OlzSystemTest\//i',
28            '/bot\//i',
29            '/crawler\//i',
30        ];
31    }
32
33    public function isBot(string $user_agent): bool {
34        foreach ($this->getBotRegexes() as $regex) {
35            if (preg_match($regex, $user_agent)) {
36                return true;
37            }
38        }
39        return false;
40    }
41
42    /** @param array<string> $get_params */
43    public function countRequest(Request $request, array $get_params = []): void {
44        $user_agent = $this->server()['HTTP_USER_AGENT'] ?? '';
45        if ($this->isBot($user_agent)) {
46            $this->log()->debug("Counter: user agent is bot: {$user_agent}");
47            return;
48        }
49        $path = "{$request->getBasePath()}{$request->getPathInfo()}";
50        $query = [];
51        foreach ($get_params as $key) {
52            $value = $request->query->get($key);
53            if ($value !== null) {
54                $query[] = "{$key}={$value}";
55            }
56        }
57        $pretty_query = empty($query) ? '' : '?'.implode('&', $query);
58        $counter_repo = $this->entityManager()->getRepository(Counter::class);
59        $counter_repo->record("{$path}{$pretty_query}");
60        $this->log()->debug("Counter: Counted {$path}{$pretty_query} (user agent: {$user_agent})");
61    }
62
63    public function dieWithHttpError(int $http_status_code): void {
64        $this->sendHttpResponseCode($http_status_code);
65
66        $out = OlzErrorPage::render([
67            'http_status_code' => $http_status_code,
68        ]);
69
70        $this->sendHttpBody($out);
71        $this->exitExecution();
72    }
73
74    public function redirect(string $redirect_url, int $http_status_code = 301): void {
75        $this->sendHeader("Location: {$redirect_url}");
76        $this->sendHttpResponseCode($http_status_code);
77
78        $out = "";
79        $out .= OlzHeaderWithoutRouting::render([
80            'title' => "Weiterleitung...",
81        ]);
82
83        $enc_redirect_url = json_encode($redirect_url);
84        $out .= <<<ZZZZZZZZZZ
85            <div class='content-full'>
86                <h2>Automatische Weiterleitung...</h2>
87                <p>Falls die automatische Weiterleitung nicht funktionieren sollte, kannst du auch diesen Link anklicken:</p>
88                <p><b><a href='{$redirect_url}' class='linkint' id='redirect-link'>{$redirect_url}</a></b></p>
89                <script type='text/javascript'>
90                    window.setTimeout(function () {
91                        window.location.href = {$enc_redirect_url};
92                    }, 1000);
93                </script>
94            </div>
95            ZZZZZZZZZZ;
96
97        $out .= OlzFooter::render();
98        $this->sendHttpBody($out);
99        $this->exitExecution();
100    }
101
102    /**
103     * @template T of array
104     *
105     * @param class-string<HttpParams<T>>             $params_class
106     * @param ?array<string, ?(string|array<string>)> $get_params
107     * @param array{just_log?: bool}                  $options
108     *
109     * @return T
110     */
111    public function validateGetParams(string $params_class, ?array $get_params = null, array $options = []): array {
112        if ($get_params === null) {
113            $get_params = $this->getParams();
114        }
115        $class_info = new \ReflectionClass($params_class);
116        $utils = new PhpStanUtils();
117        $php_doc_node = $utils->parseDocComment(
118            $class_info->getDocComment(),
119            $class_info->getFileName() ?: null,
120        );
121        $type = $php_doc_node?->getExtendsTagValues()[0]->type->genericTypes[0];
122        $aliases = $utils->getAliases($php_doc_node);
123        if (!$type) {
124            $this->dieWithHttpError(400);
125            throw new \Exception('should already have failed');
126        }
127        $result = ValidateVisitor::validateDeserialize($utils, $get_params, $type, $aliases);
128        if (!$result->isValid() && ($options['just_log'] ?? false) === false) {
129            $this->dieWithHttpError(400);
130            throw new \Exception('should already have failed');
131        }
132        return $result->getValue();
133    }
134
135    // @codeCoverageIgnoreStart
136    // Reason: Mock functions for tests.
137
138    protected function sendHttpResponseCode(int $http_response_code): void {
139        http_response_code($http_response_code);
140    }
141
142    protected function sendHeader(string $http_header_line): void {
143        header($http_header_line);
144    }
145
146    protected function sendHttpBody(string $http_body): void {
147        echo $http_body;
148    }
149
150    protected function exitExecution(): void {
151        exit('');
152    }
153
154    // @codeCoverageIgnoreEnd
155}