@@ -48,9 +48,24 @@ class Request implements \ArrayAccess, \Countable, IRequest {
4848 // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
4949 public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*# ' ;
5050 public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$# ' ;
51+
5152 public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/ ' ;
53+
54+ /**
55+ * Whether the raw PUT body stream has already been returned.
56+ */
5257 private bool $ isPutStreamContentAlreadySent = false ;
58+
59+ /**
60+ * Internal request data store.
61+ */
5362 protected array $ items = [];
63+
64+ /**
65+ * Magic properties that are exposed directly from $items.
66+ *
67+ * @var list<string>
68+ */
5469 protected array $ allowedKeys = [
5570 'get ' ,
5671 'post ' ,
@@ -64,24 +79,27 @@ class Request implements \ArrayAccess, \Countable, IRequest {
6479 'requesttoken ' ,
6580 ];
6681
82+ /**
83+ * Whether request-body decoding has already been attempted.
84+ */
6785 protected bool $ contentDecoded = false ;
86+
87+ /**
88+ * Deferred decoding error from the request body, if any.
89+ */
6890 private ?\JsonException $ decodingException = null ;
6991
7092 /**
71- * @param array $vars An associative array with the following optional values :
72- * - array 'urlParams' the parameters which were matched from the URL
93+ * @param array $vars Associative request data with the following optional keys :
94+ * - array 'urlParams' route parameters extracted from the URL
7395 * - array 'get' the $_GET array
74- * - array|string 'post' the $_POST array or JSON string
96+ * - array 'post' the $_POST array
7597 * - array 'files' the $_FILES array
7698 * - array 'server' the $_SERVER array
7799 * - array 'env' the $_ENV array
78100 * - array 'cookies' the $_COOKIE array
79- * - string 'method' the request method (GET, POST etc)
80- * - string|false 'requesttoken' the requesttoken or false when not available
81- * @param IRequestId $requestId
82- * @param IConfig $config
83- * @param CsrfTokenManager|null $csrfTokenManager
84- * @param string $inputStream
101+ * - string 'method' the HTTP request method, for example GET or POST
102+ * - string|false 'requesttoken' the request token, or false if unavailable
85103 * @see https://www.php.net/manual/en/reserved.variables.php
86104 */
87105 public function __construct (
@@ -108,8 +126,16 @@ public function __construct(
108126 $ this ->items ['params ' ]
109127 );
110128 }
129+
111130 /**
131+ * Replaces the current URL parameters and merges them into the parameter set.
132+ *
133+ * URL parameters take precedence over previously merged values with the same
134+ * key.
135+ *
112136 * @param array $parameters
137+ *
138+ * @internal public only so it can be consumed by OC\AppFramework\App
113139 */
114140 public function setUrlParameters (array $ parameters ) {
115141 $ this ->items ['urlParams ' ] = $ parameters ;
@@ -120,42 +146,30 @@ public function setUrlParameters(array $parameters) {
120146 }
121147
122148 /**
123- * Countable method
124- * @return int
149+ * Returns the number of merged request parameters.
125150 */
126151 #[\Override]
127152 public function count (): int {
128153 return \count ($ this ->items ['parameters ' ]);
129154 }
130155
131156 /**
132- * ArrayAccess methods
157+ * Whether a merged request parameter exists.
133158 *
134- * Gives access to the combined GET, POST and urlParams arrays
159+ * ArrayAccess operates on the merged parameter set.
135160 *
136- * Examples:
137- *
138- * $var = $request['myvar'];
139- *
140- * or
141- *
142- * if(!isset($request['myvar']) {
143- * // Do something
144- * }
145- *
146- * $request['myvar'] = 'something'; // This throws an exception.
147- *
148- * @param string $offset The key to lookup
149- * @return boolean
161+ * @param string $offset Parameter name
162+ * @return bool
150163 */
151164 #[\Override]
152165 public function offsetExists ($ offset ): bool {
153166 return isset ($ this ->items ['parameters ' ][$ offset ]);
154167 }
155168
156169 /**
157- * @see offsetExists
158- * @param string $offset
170+ * Returns a merged request parameter value, or null if it is missing.
171+ *
172+ * @param string $offset Parameter name
159173 * @return mixed
160174 */
161175 #[\Override]
@@ -165,7 +179,8 @@ public function offsetGet($offset) {
165179 }
166180
167181 /**
168- * @see offsetExists
182+ * Request objects are immutable.
183+ *
169184 * @param string $offset
170185 * @param mixed $value
171186 */
@@ -175,7 +190,8 @@ public function offsetSet($offset, $value): void {
175190 }
176191
177192 /**
178- * @see offsetExists
193+ * Request objects are immutable.
194+ *
179195 * @param string $offset
180196 */
181197 #[\Override]
@@ -184,7 +200,8 @@ public function offsetUnset($offset): void {
184200 }
185201
186202 /**
187- * Magic property accessors
203+ * Request objects are immutable.
204+ *
188205 * @param string $name
189206 * @param mixed $value
190207 */
@@ -193,17 +210,16 @@ public function __set($name, $value) {
193210 }
194211
195212 /**
196- * Access request variables by method and name.
197- * Examples:
213+ * Returns request data through magic property access.
198214 *
199- * $request->post['myvar']; // Only look for POST variables
200- * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
201- * Looks in the combined GET, POST and urlParams array .
215+ * Named properties read from the merged parameter set. Method-specific
216+ * properties (`get`, `post`, `put`, `patch`) are only available for the
217+ * matching HTTP method and throw a \LogicException otherwise .
202218 *
203- * If you access e.g. ->post but the current HTTP request method
204- * is GET a \LogicException will be thrown .
219+ * Depending on the method and content type, `put` may return either parsed
220+ * parameters or a readable stream for the raw request body .
205221 *
206- * @param string $name The key to look for.
222+ * @param string $name Property name
207223 * @throws \LogicException
208224 * @return mixed|null
209225 */
@@ -238,6 +254,8 @@ public function __get($name) {
238254 }
239255
240256 /**
257+ * Whether a magic property is available.
258+ *
241259 * @param string $name
242260 * @return bool
243261 */
@@ -249,6 +267,8 @@ public function __isset($name) {
249267 }
250268
251269 /**
270+ * Request objects are immutable.
271+ *
252272 * @param string $id
253273 */
254274 public function __unset ($ id ) {
@@ -312,18 +332,17 @@ public function getCookie(string $key) {
312332 }
313333
314334 /**
315- * Returns the request body content.
335+ * Returns request body content for method-specific magic accessors .
316336 *
317- * If the HTTP request method is PUT and the body
318- * not application/x-www-form-urlencoded or application/json a stream
319- * resource is returned, otherwise an array.
320- *
321- * @return array|string|resource The request body content or a resource to read the body stream.
337+ * For PUT requests with a non-empty body that is neither JSON nor
338+ * form-encoded, a readable stream resource for the raw body is returned.
339+ * Otherwise, parsed parameters are returned as an array.
322340 *
341+ * @return array|string|resource The request body content or a resource for the raw body stream
323342 * @throws \LogicException
324343 */
325344 protected function getContent () {
326- // If the content can't be parsed into an array then return a stream resource .
345+ // If the content cannot be parsed into parameters, return a raw body stream .
327346 if ($ this ->isPutStreamContent ()) {
328347 if ($ this ->isPutStreamContentAlreadySent ) {
329348 throw new \LogicException (
@@ -348,15 +367,21 @@ private function isPutStreamContent(): bool {
348367 }
349368
350369 /**
351- * Attempt to decode the content and populate parameters
370+ * Decodes the request body, if applicable, and merges decoded parameters
371+ * into the parameter set.
372+ *
373+ * JSON-compatible content types are decoded from the input stream. For
374+ * non-GET and non-POST form-encoded requests, the input stream is parsed
375+ * into parameters. Decoding errors are stored and can later be rethrown via
376+ * throwDecodingExceptionIfAny().
352377 */
353378 protected function decodeContent () {
354379 if ($ this ->contentDecoded ) {
355380 return ;
356381 }
357382 $ params = [];
358383
359- // 'application/json' and other JSON-related content types must be decoded manually.
384+ // JSON-compatible content types must be decoded manually.
360385 if (preg_match (self ::JSON_CONTENT_TYPE_REGEX , $ this ->getHeader ('Content-Type ' )) === 1 ) {
361386 $ content = file_get_contents ($ this ->inputStream );
362387 if ($ content !== '' ) {
@@ -372,8 +397,7 @@ protected function decodeContent() {
372397 $ this ->items ['post ' ] = $ params ;
373398 }
374399 }
375- // Handle application/x-www-form-urlencoded for methods other than GET
376- // or post correctly
400+ // Handle form-encoded request bodies for methods other than GET and POST.
377401 } elseif ($ this ->method !== 'GET '
378402 && $ this ->method !== 'POST '
379403 && str_contains ($ this ->getHeader ('Content-Type ' ), 'application/x-www-form-urlencoded ' )) {
@@ -426,7 +450,7 @@ public function passesCSRFCheck(): bool {
426450 }
427451
428452 /**
429- * Whether the cookie checks are required
453+ * Whether cookie-based same-site checks are required for this request.
430454 */
431455 private function cookieCheckRequired (): bool {
432456 if ($ this ->getHeader ('OCS-APIREQUEST ' )) {
@@ -440,14 +464,14 @@ private function cookieCheckRequired(): bool {
440464 }
441465
442466 /**
443- * Wrapper around session_get_cookie_params
467+ * Wrapper around session_get_cookie_params().
444468 */
445469 public function getCookieParams (): array {
446470 return session_get_cookie_params ();
447471 }
448472
449473 /**
450- * Appends the __Host- prefix to the cookie if applicable
474+ * Returns the cookie name with the __Host- prefix applied when appropriate.
451475 */
452476 protected function getProtectedCookieName (string $ name ): string {
453477 $ cookieParams = $ this ->getCookieParams ();
@@ -492,16 +516,19 @@ public function getId(): string {
492516 }
493517
494518 /**
495- * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
496- * For details regarding what "match" means, refer to `matchesTrustedProxy`.
497- * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
519+ * Checks whether the given remote address matches one of the configured
520+ * trusted proxies.
521+ *
522+ * Invalid trusted proxy configuration is treated as non-matching.
523+ *
524+ * @return bool true if $remoteAddress matches a trusted proxy, false otherwise
498525 */
499526 protected function isTrustedProxy ($ trustedProxies , $ remoteAddress ) {
500527 try {
501528 return IpUtils::checkIp ($ remoteAddress , $ trustedProxies );
502529 } catch (\Throwable ) {
503- // We can not log to our log here as the logger is using `getRemoteAddress` which uses the function, so we would have a cyclic dependency
504- // Reaching this line means `trustedProxies` is in invalid format .
530+ // Cannot log through the regular logger here because it may depend on
531+ // getRemoteAddress(), which would create a cyclic dependency .
505532 error_log ('Nextcloud trustedProxies has malformed entries ' );
506533 return false ;
507534 }
@@ -550,9 +577,6 @@ public function getRemoteAddress(): string {
550577 return $ remoteAddress ;
551578 }
552579
553- /**
554- * Check overwrite condition
555- */
556580 private function isOverwriteCondition (): bool {
557581 $ regex = '/ ' . $ this ->config ->getSystemValueString ('overwritecondaddr ' , '' ) . '/ ' ;
558582 $ remoteAddr = isset ($ this ->server ['REMOTE_ADDR ' ]) ? $ this ->server ['REMOTE_ADDR ' ] : '' ;
@@ -750,11 +774,9 @@ public function getServerHost(): string {
750774 }
751775
752776 /**
753- * Returns the overwritehost setting from the config if set and
754- * if the overwrite condition is met
777+ * Returns the overwritehost config value if configured and applicable.
755778 *
756- * @return string|null overwritehost value or null if not defined or the defined condition
757- * isn't met
779+ * @return string|null
758780 */
759781 private function getOverwriteHost () {
760782 if ($ this ->config ->getSystemValueString ('overwritehost ' ) !== '' && $ this ->isOverwriteCondition ()) {
0 commit comments