RequestParameterExtractor::getRepeatedParam()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 18
rs 10
1
<?php declare(strict_types=1);
2
/**
3
 * ownCloud
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Pauli Järvinen <[email protected]>
9
 * @copyright Pauli Järvinen 2021 - 2025
10
 */
11
12
namespace OCA\Music\AppFramework\Utility;
13
14
use OCP\IRequest;
15
16
/**
17
 * Reads and parses annotations from doc comments
18
 */
19
class RequestParameterExtractor {
20
	private IRequest $request;
21
	private array $customFilters;
22
23
	public function __construct(IRequest $request, array $customFilters = []) {
24
		$this->request = $request;
25
		$this->customFilters = $customFilters;
26
	}
27
28
	/**
29
	 * @param object|string $object an object or classname
30
	 * @param string $method the method for which we want to extract parameters from the HTTP request
31
	 * @throws RequestParameterExtractorException if a required parameter is not found from the request
32
	 * @return array of mixed types (string, string[], int, bool, null)
33
	 */
34
	public function getParametersForMethod($object, string $method) : array {
35
		$refMethod = new \ReflectionMethod($object, $method);
36
		return \array_map([$this, 'getParameterValueFromRequest'], $refMethod->getParameters());
37
	}
38
39
	/**
40
	 * @throws RequestParameterExtractorException
41
	 * @return string|string[]|int|bool|null
42
	 */
43
	private function getParameterValueFromRequest(\ReflectionParameter $parameter) {
44
		$paramName = $parameter->getName();
45
		$type = $parameter->getType();
46
		if ($type === null) {
47
			$typeName = null;
48
		} else {
49
			\assert($type instanceof \ReflectionNamedType); // we don't use union types introduced in PHP8
50
			$typeName = $type->getName();
51
		}
52
53
		if ($typeName === 'array') {
54
			$parameterValue = $this->getRepeatedParam($paramName);
55
		} else {
56
			$parameterValue = $this->request->getParam($paramName);
57
		}
58
59
		if (\array_key_exists($paramName, $this->customFilters)) {
60
			$parameterValue = $this->customFilters[$paramName]($parameterValue);
61
		}
62
63
		if ($parameterValue === null) {
64
			if ($parameter->isOptional()) {
65
				$parameterValue = $parameter->getDefaultValue();
66
			} elseif (!$parameter->allowsNull()) {
67
				throw new RequestParameterExtractorException("Required parameter '$paramName' missing");
68
			}
69
		} else {
70
			// cast non-null values to requested type
71
			if ($typeName === 'int' || $typeName === 'integer') {
72
				$parameterValue = (int)$parameterValue;
73
			} elseif ($typeName === 'bool' || $typeName === 'boolean') {
74
				$parameterValue = \filter_var($parameterValue, FILTER_VALIDATE_BOOLEAN);
75
			}
76
		}
77
78
		return $parameterValue;
79
	}
80
81
	/**
82
	 * Get values for parameter which may be present multiple times in the query string or POST data.
83
	 * @return string[]
84
	 */
85
	private function getRepeatedParam(string $paramName) : array {
86
		// We can't use the IRequest object nor $_GET and $_POST to get the data
87
		// because all of these are based to the idea of unique parameter names.
88
		// If the same name is repeated, only the last value is saved. Hence, we
89
		// need to parse the raw data manually.
90
91
		// query string is always present (although it could be empty)
92
		$values = self::parseRepeatedKeyValues($paramName, $_SERVER['QUERY_STRING']);
93
94
		// POST data is available if the method is POST
95
		if ($this->request->getMethod() == 'POST') {
96
			$values = \array_merge(
97
				$values,
98
				self::parseRepeatedKeyValues($paramName, \file_get_contents('php://input'))
99
			);
100
		}
101
102
		return $values;
103
	}
104
105
	/**
106
	 * Parse a string like "someKey=value1&someKey=value2&anotherKey=valueA&someKey=value3"
107
	 * and return an array of values for the given key
108
	 * @return string[]
109
	 */
110
	private static function parseRepeatedKeyValues(string $key, string $data) : array {
111
		$result = [];
112
113
		$keyValuePairs = \explode('&', $data);
114
115
		foreach ($keyValuePairs as $pair) {
116
			$keyAndValue = \explode('=', $pair);
117
118
			if ($keyAndValue[0] == $key) {
119
				$result[] = $keyAndValue[1];
120
			}
121
		}
122
123
		return $result;
124
	}
125
}
126