Passed
Push — main ( 6d966f...8d9c2b )
by Greg
06:48
created

Validator::queryParams()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2021 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees;
21
22
use Closure;
23
use LogicException;
24
use Psr\Http\Message\ServerRequestInterface;
25
26
use function array_reduce;
27
use function ctype_digit;
28
use function is_array;
29
use function is_int;
30
use function is_string;
31
use function parse_url;
32
use function str_starts_with;
33
34
/**
35
 * Validate a parameter from an HTTP request
36
 */
37
class Validator
38
{
39
    /** @var array<string|array> */
40
    private array $parameters;
41
42
    /** @var array<Closure> */
43
    private array $rules = [];
44
45
    /**
46
     * @param array<string|array> $parameters
47
     */
48
    public function __construct(array $parameters)
49
    {
50
        $this->parameters = $parameters;
51
    }
52
53
    /**
54
     * @param ServerRequestInterface $request
55
     *
56
     * @return self
57
     */
58
    public static function parsedBody(ServerRequestInterface $request): self
59
    {
60
        return new self((array) $request->getParsedBody());
61
    }
62
63
    /**
64
     * @param ServerRequestInterface $request
65
     *
66
     * @return self
67
     */
68
    public static function queryParams(ServerRequestInterface $request): self
69
    {
70
        return new self($request->getQueryParams());
71
    }
72
73
    /**
74
     * @param int $minimum
75
     * @param int $maximum
76
     *
77
     * @return self
78
     */
79
    public function isBetween(int $minimum, int $maximum): self
80
    {
81
        $this->rules[] = static function ($value) use ($minimum, $maximum): ?int {
82
            if (is_int($value)) {
83
                if ($value < $minimum || $value > $maximum) {
84
                    return null;
85
                }
86
87
                return $value;
88
            }
89
90
            throw new LogicException('Validator::isBetween() can only be used for integers');
91
        };
92
93
        return $this;
94
    }
95
96
    public function localUrl(string $base_url): self
97
    {
98
        $this->rules[] = static function ($value) use ($base_url): ?string {
99
            if (is_string($value)) {
100
                $value_info    = parse_url($value);
101
                $base_url_info = parse_url($base_url);
102
103
                if (!is_array($base_url_info)) {
1 ignored issue
show
introduced by
The condition is_array($base_url_info) is always true.
Loading history...
104
                    throw new LogicException(__METHOD__ . ' needs a valid URL');
105
                }
106
107
                if (is_array($value_info)) {
1 ignored issue
show
introduced by
The condition is_array($value_info) is always true.
Loading history...
108
                    $scheme_ok = ($value_info['scheme'] ?? 'http') === ($base_url_info['scheme'] ?? 'http');
109
                    $host_ok   = ($value_info['host'] ?? '') === ($base_url_info['host'] ?? '');
110
                    $port_ok   = ($value_info['port'] ?? '') === ($base_url_info['port'] ?? '');
111
                    $user_ok   = ($value_info['user'] ?? '') === ($base_url_info['user'] ?? '');
112
                    $path_ok   = str_starts_with($value_info['path'] ?? '/', $base_url_info['path'] ?? '/');
113
114
                    if ($scheme_ok && $host_ok && $port_ok && $user_ok && $path_ok) {
115
                        return $value;
116
                    }
117
                }
118
119
                return null;
120
            }
121
122
            throw new LogicException(__METHOD__ . ' can only be used for strings');
123
        };
124
125
        return $this;
126
    }
127
128
    /**
129
     * @param string $parameter
130
     *
131
     * @return array<string>
132
     */
133
    public function array(string $parameter): array
134
    {
135
        $value = $this->parameters[$parameter] ?? null;
136
137
        if (!is_array($value)) {
138
            $value = [];
139
        }
140
141
        return array_reduce($this->rules, static fn($rule) => $rule($value), $value);
142
    }
143
144
    /**
145
     * @param string $parameter
146
     *
147
     * @return int|null
148
     */
149
    public function integer(string $parameter): ?int
150
    {
151
        $value = $this->parameters[$parameter] ?? null;
152
153
        if (is_string($value) && ctype_digit($value)) {
154
            $value = (int) $value;
155
        } else {
156
            $value = null;
157
        }
158
159
        return array_reduce($this->rules, static fn($rule) => $rule($value), $value);
160
    }
161
162
    /**
163
     * @param string $parameter
164
     *
165
     * @return string|null
166
     */
167
    public function string(string $parameter): ?string
168
    {
169
        $value = $this->parameters[$parameter] ?? null;
170
171
        if (!is_string($value)) {
172
            $value = null;
173
        }
174
175
        return array_reduce($this->rules, static fn($value, $rule) => $rule($value), $value);
176
    }
177
}
178