Passed
Push — master ( 4a5f25...dc1961 )
by Pauli
02:27
created

RequestParameterExtractor   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 97
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 17
eloc 38
c 1
b 0
f 0
dl 0
loc 97
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getParametersForMethod() 0 3 1
A __construct() 0 3 1
A getRepeatedParam() 0 16 2
B getParameterValueFromRequest() 0 30 10
A parseRepeatedKeyValues() 0 14 3
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
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 $request;
21
	private $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, 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|int|bool|null
42
	 */
43
	private function getParameterValueFromRequest(\ReflectionParameter $parameter) {
44
		$paramName = $parameter->getName();
45
		$type = (string)$parameter->getType();
46
47
		if ($type === 'array') {
48
			$parameterValue = $this->getRepeatedParam($paramName);
49
		} else {
50
			$parameterValue = $this->request->getParam($paramName);
51
		}
52
53
		if (\array_key_exists($paramName, $this->customFilters)) {
54
			$parameterValue = $this->customFilters[$paramName]($parameterValue);
55
		}
56
57
		if ($parameterValue === null) {
58
			if ($parameter->isOptional()) {
59
				$parameterValue = $parameter->getDefaultValue();
60
			} elseif (!$parameter->allowsNull()) {
61
				throw new RequestParameterExtractorException("Required parameter '$paramName' missing");
62
			}
63
		} else {
64
			// cast non-null values to requested type
65
			if ($type === 'int' || $type === 'integer') {
66
				$parameterValue = (int)$parameterValue;
67
			} elseif ($type === 'bool' || $type === 'boolean') {
68
				$parameterValue = \filter_var($parameterValue, FILTER_VALIDATE_BOOLEAN);
69
			}
70
		}
71
72
		return $parameterValue;
73
	}
74
75
	/**
76
	 * Get values for parameter which may be present multiple times in the query string or POST data.
77
	 * @return string[]
78
	 */
79
	private function getRepeatedParam(string $paramName) : array {
80
		// We can't use the IRequest object nor $_GET and $_POST to get the data
81
		// because all of these are based to the idea of unique parameter names.
82
		// If the same name is repeated, only the last value is saved. Hence, we
83
		// need to parse the raw data manually.
84
		
85
		// query string is always present (although it could be empty)
86
		$values = self::parseRepeatedKeyValues($paramName, $_SERVER['QUERY_STRING']);
87
88
		// POST data is available if the method is POST
89
		if ($this->request->getMethod() == 'POST') {
90
			$values = \array_merge($values,
91
					self::parseRepeatedKeyValues($paramName, \file_get_contents('php://input')));
92
		}
93
94
		return $values;
95
	}
96
97
	/**
98
	 * Parse a string like "someKey=value1&someKey=value2&anotherKey=valueA&someKey=value3"
99
	 * and return an array of values for the given key
100
	 * @return string[]
101
	 */
102
	private static function parseRepeatedKeyValues(string $key, string $data) : array {
103
		$result = [];
104
105
		$keyValuePairs = \explode('&', $data);
106
107
		foreach ($keyValuePairs as $pair) {
108
			$keyAndValue = \explode('=', $pair);
109
			
110
			if ($keyAndValue[0] == $key) {
111
				$result[] = $keyAndValue[1];
112
			}
113
		}
114
115
		return $result;
116
	}
117
}
118