Completed
Push — 3.7 ( 8f9bb9...f2b894 )
by Maxime
05:14
created

SS_HTTPRequest::isValidHttpMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * Represents a HTTP-request, including a URL that is tokenised for parsing, and a request method
5
 * (GET/POST/PUT/DELETE). This is used by {@link RequestHandler} objects to decide what to do.
6
 *
7
 * Caution: objects of this class are immutable, e.g. echo $request['a']; works as expected,
8
 * but $request['a'] = '1'; has no effect.
9
 *
10
 * The intention is that a single SS_HTTPRequest object can be passed from one object to another, each object calling
11
 * match() to get the information that they need out of the URL.  This is generally handled by
12
 * {@link RequestHandler::handleRequest()}.
13
 *
14
 * @package framework
15
 * @subpackage control
16
 */
17
class SS_HTTPRequest implements ArrayAccess {
18
19
	/**
20
	 * @var string $url
21
	 */
22
	protected $url;
23
24
	/**
25
	 * The non-extension parts of the passed URL as an array, originally exploded by the "/" separator.
26
	 * All elements of the URL are loaded in here,
27
	 * and subsequently popped out of the array by {@link shift()}.
28
	 * Only use this structure for internal request handling purposes.
29
	 */
30
	protected $dirParts;
31
32
	/**
33
	 * @var string $extension The URL extension (if present)
34
	 */
35
	protected $extension;
36
37
	/**
38
	 * @var string $httpMethod The HTTP method in all uppercase: GET/PUT/POST/DELETE/HEAD
39
	 */
40
	protected $httpMethod;
41
42
	/**
43
	 * @var array $getVars Contains alls HTTP GET parameters passed into this request.
44
	 */
45
	protected $getVars = array();
46
47
	/**
48
	 * @var array $postVars Contains alls HTTP POST parameters passed into this request.
49
	 */
50
	protected $postVars = array();
51
52
	/**
53
	 * HTTP Headers like "Content-Type: text/xml"
54
	 *
55
	 * @see http://en.wikipedia.org/wiki/List_of_HTTP_headers
56
	 * @var array
57
	 */
58
	protected $headers = array();
59
60
	/**
61
	 * Raw HTTP body, used by PUT and POST requests.
62
	 *
63
	 * @var string
64
	 */
65
	protected $body;
66
67
	/**
68
	 * @var array $allParams Contains an associative array of all
69
	 * arguments matched in all calls to {@link RequestHandler->handleRequest()}.
70
	 * It's a "historical record" that's specific to the current call of
71
	 * {@link handleRequest()}, and is only complete once the "last call" to that method is made.
72
	 */
73
	protected $allParams = array();
74
75
	/**
76
	 * @var array $latestParams Contains an associative array of all
77
	 * arguments matched in the current call from {@link RequestHandler->handleRequest()},
78
	 * as denoted with a "$"-prefix in the $url_handlers definitions.
79
	 * Contains different states throughout its lifespan, so just useful
80
	 * while processed in {@link RequestHandler} and to get the last
81
	 * processes arguments.
82
	 */
83
	protected $latestParams = array();
84
85
	/**
86
	 * @var array $routeParams Contains an associative array of all arguments
87
	 * explicitly set in the route table for the current request.
88
	 * Useful for passing generic arguments via custom routes.
89
	 *
90
	 * E.g. The "Locale" parameter would be assigned "en_NZ" below
91
	 *
92
	 * Director:
93
	 *   rules:
94
	 *     'en_NZ/$URLSegment!//$Action/$ID/$OtherID':
95
	 *       Controller: 'ModelAsController'
96
	 *       Locale: 'en_NZ'
97
	 */
98
	protected $routeParams = array();
99
100
	protected $unshiftedButParsedParts = 0;
101
102
	/**
103
	 * Construct a SS_HTTPRequest from a URL relative to the site root.
104
	 */
105
	public function __construct($httpMethod, $url, $getVars = array(), $postVars = array(), $body = null) {
106
		$this->httpMethod = strtoupper($httpMethod);
107
		$this->setUrl($url);
108
109
		$this->getVars = (array) $getVars;
110
		$this->postVars = (array) $postVars;
111
		$this->body = $body;
112
	}
113
114
	/**
115
	 * Allow the setting of a URL
116
	 *
117
	 * This is here so that RootURLController can change the URL of the request
118
	 * without us loosing all the other info attached (like headers)
119
	 *
120
	 * @param string The new URL
121
	 *
122
	 * @return SS_HTTPRequest The updated request
123
	 */
124
	public function setUrl($url) {
125
		$this->url = $url;
126
127
		// Normalize URL if its relative (strictly speaking), or has leading slashes
128
		if(Director::is_relative_url($url) || preg_match('/^\//', $url)) {
129
			$this->url = preg_replace(array('/\/+/','/^\//', '/\/$/'),array('/','',''), $this->url);
130
		}
131
		if(preg_match('/^(.+)\.([A-Za-z][A-Za-z0-9]*)$/', $this->url, $matches)) {
132
			$this->url = $matches[1];
133
			$this->extension = $matches[2];
134
		}
135
		if($this->url) $this->dirParts = preg_split('|/+|', $this->url);
136
		else $this->dirParts = array();
137
138
		return $this;
139
	}
140
141
	/**
142
	 * @return bool
143
	 */
144
	public function isGET() {
145
		return $this->httpMethod == 'GET';
146
	}
147
148
	/**
149
	 * @return bool
150
	 */
151
	public function isPOST() {
152
		return $this->httpMethod == 'POST';
153
	}
154
155
	/**
156
	 * @return bool
157
	 */
158
	public function isPUT() {
159
		return $this->httpMethod == 'PUT';
160
	}
161
162
	/**
163
	 * @return bool
164
	 */
165
	public function isDELETE() {
166
		return $this->httpMethod == 'DELETE';
167
	}
168
169
	/**
170
	 * @return bool
171
	 */
172
	public function isHEAD() {
173
		return $this->httpMethod == 'HEAD';
174
	}
175
176
	/**
177
	 * @param string $body
178
	 * @return SS_HTTPRequest $this
179
	 */
180
	public function setBody($body) {
181
		$this->body = $body;
182
		return $this;
183
	}
184
185
	/**
186
	 * @return null|string
187
	 */
188
	public function getBody() {
189
		return $this->body;
190
	}
191
192
	/**
193
	 * @return array
194
	 */
195
	public function getVars() {
196
		return $this->getVars;
197
	}
198
199
	/**
200
	 * @return array
201
	 */
202
	public function postVars() {
203
		return $this->postVars;
204
	}
205
206
	/**
207
	 * Returns all combined HTTP GET and POST parameters
208
	 * passed into this request. If a parameter with the same
209
	 * name exists in both arrays, the POST value is returned.
210
	 *
211
	 * @return array
212
	 */
213
	public function requestVars() {
214
		return ArrayLib::array_merge_recursive($this->getVars, $this->postVars);
215
	}
216
217
	/**
218
	 * @param string $name
219
	 * @return mixed
220
	 */
221
	public function getVar($name) {
222
		if(isset($this->getVars[$name])) return $this->getVars[$name];
223
	}
224
225
	/**
226
	 * @param string $name
227
	 * @return mixed
228
	 */
229
	public function postVar($name) {
230
		if(isset($this->postVars[$name])) return $this->postVars[$name];
231
	}
232
233
	/**
234
	 * @param string $name
235
	 * @return mixed
236
	 */
237
	public function requestVar($name) {
238
		if(isset($this->postVars[$name])) return $this->postVars[$name];
239
		if(isset($this->getVars[$name])) return $this->getVars[$name];
240
	}
241
242
	/**
243
	 * Returns a possible file extension found in parsing the URL
244
	 * as denoted by a "."-character near the end of the URL.
245
	 * Doesn't necessarily have to belong to an existing file,
246
	 * as extensions can be also used for content-type-switching.
247
	 *
248
	 * @return string
249
	 */
250
	public function getExtension() {
251
		return $this->extension;
252
	}
253
254
	/**
255
	 * Checks if the {@link SS_HTTPRequest->getExtension()} on this request matches one of the more common media types
256
	 * embedded into a webpage - e.g. css, png.
257
	 *
258
	 * This is useful for things like determining wether to display a fully rendered error page or not. Note that the
259
	 * media file types is not at all comprehensive.
260
	 *
261
	 * @return bool
262
	 */
263
	public function isMedia() {
264
		return in_array($this->getExtension(), array('css', 'js', 'jpg', 'jpeg', 'gif', 'png', 'bmp', 'ico'));
265
	}
266
267
	/**
268
	 * Add a HTTP header to the response, replacing any header of the same name.
269
	 *
270
	 * @param string $header Example: "Content-Type"
271
	 * @param string $value Example: "text/xml"
272
	 */
273
	public function addHeader($header, $value) {
274
		$this->headers[$header] = $value;
275
	}
276
277
	/**
278
	 * @return array
279
	 */
280
	public function getHeaders() {
281
		return $this->headers;
282
	}
283
284
	/**
285
	 * Remove an existing HTTP header
286
	 *
287
	 * @param string $header
288
	 * @return mixed
289
	 */
290
	public function getHeader($header) {
291
		return (isset($this->headers[$header])) ? $this->headers[$header] : null;
292
	}
293
294
	/**
295
	 * Remove an existing HTTP header by its name,
296
	 * e.g. "Content-Type".
297
	 *
298
	 * @param string $header
299
	 * @return SS_HTTPRequest $this
300
	 */
301
	public function removeHeader($header) {
302
		if(isset($this->headers[$header])) unset($this->headers[$header]);
303
		return $this;
304
	}
305
306
	/**
307
	 * Returns the URL used to generate the page
308
	 *
309
	 * @param bool $includeGetVars whether or not to include the get parameters\
310
	 * @return string
311
	 */
312
	public function getURL($includeGetVars = false) {
313
		$url = ($this->getExtension()) ? $this->url . '.' . $this->getExtension() : $this->url;
314
315
		if ($includeGetVars) {
316
			// if we don't unset $vars['url'] we end up with /my/url?url=my/url&foo=bar etc
317
318
			$vars = $this->getVars();
319
			unset($vars['url']);
320
321
			if (count($vars)) {
322
				$url .= '?' . http_build_query($vars);
323
			}
324
		}
325
		else if(strpos($url, "?") !== false) {
326
			$url = substr($url, 0, strpos($url, "?"));
327
		}
328
329
		return $url;
330
	}
331
332
	/**
333
	 * Returns true if this request an ajax request,
334
	 * based on custom HTTP ajax added by common JavaScript libraries,
335
	 * or based on an explicit "ajax" request parameter.
336
	 *
337
	 * @return boolean
338
	 */
339
	public function isAjax() {
340
		return (
341
			$this->requestVar('ajax') ||
342
			$this->getHeader('X-Requested-With') && $this->getHeader('X-Requested-With') == "XMLHttpRequest"
343
		);
344
	}
345
346
	/**
347
	 * Enables the existence of a key-value pair in the request to be checked using
348
	 * array syntax, so isset($request['title']) will check for $_POST['title'] and $_GET['title']
349
	 *
350
	 * @param unknown_type $offset
351
	 * @return boolean
352
	 */
353
	public function offsetExists($offset) {
354
		if(isset($this->postVars[$offset])) return true;
355
		if(isset($this->getVars[$offset])) return true;
356
		return false;
357
	}
358
359
	/**
360
	 * Access a request variable using array syntax. eg: $request['title'] instead of $request->postVar('title')
361
	 *
362
	 * @param unknown_type $offset
363
	 * @return unknown
364
	 */
365
	public function offsetGet($offset) {
366
		return $this->requestVar($offset);
367
	}
368
369
	/**
370
	 * @ignore
371
	 */
372
	public function offsetSet($offset, $value) {}
373
374
	/**
375
	 * @ignore
376
	 */
377
	public function offsetUnset($offset) {}
378
379
	/**
380
	 * Construct an SS_HTTPResponse that will deliver a file to the client.
381
	 * Caution: Since it requires $fileData to be passed as binary data (no stream support),
382
	 * it's only advisable to send small files through this method.
383
	 *
384
	 * @static
385
	 * @param $fileData
386
	 * @param $fileName
387
	 * @param null $mimeType
388
	 * @return SS_HTTPResponse
389
	 */
390
	public static function send_file($fileData, $fileName, $mimeType = null) {
391
		if(!$mimeType) {
392
			$mimeType = HTTP::get_mime_type($fileName);
393
		}
394
		$response = new SS_HTTPResponse($fileData);
395
		$response->addHeader("Content-Type", "$mimeType; name=\"" . addslashes($fileName) . "\"");
396
		// Note a IE-only fix that inspects this header in HTTP::add_cache_headers().
397
		$response->addHeader("Content-Disposition", "attachment; filename=\"" . addslashes($fileName) . "\"");
398
		$response->addHeader("Content-Length", strlen($fileData));
399
400
		return $response;
401
	}
402
403
	/**
404
	 * Matches a URL pattern
405
	 * The pattern can contain a number of segments, separated by / (and an extension indicated by a .)
406
	 *
407
	 * The parts can be either literals, or, if they start with a $ they are interpreted as variables.
408
	 *  - Literals must be provided in order to match
409
	 *  - $Variables are optional
410
	 *  - However, if you put ! at the end of a variable, then it becomes mandatory.
411
	 *
412
	 * For example:
413
	 *  - admin/crm/list will match admin/crm/$Action/$ID/$OtherID, but it won't match admin/crm/$Action!/$ClassName!
414
	 *
415
	 * The pattern can optionally start with an HTTP method and a space.  For example, "POST $Controller/$Action".
416
	 * This is used to define a rule that only matches on a specific HTTP method.
417
	 *
418
	 * @param $pattern
419
	 * @param bool $shiftOnSuccess
420
	 * @return array|bool
421
	 */
422
	public function match($pattern, $shiftOnSuccess = false) {
423
		// Check if a specific method is required
424
		if(preg_match('/^([A-Za-z]+) +(.*)$/', $pattern, $matches)) {
425
			$requiredMethod = $matches[1];
426
			if($requiredMethod != $this->httpMethod) return false;
427
428
			// If we get this far, we can match the URL pattern as usual.
429
			$pattern = $matches[2];
430
		}
431
432
		// Special case for the root URL controller
433
		if(!$pattern) {
434
			return ($this->dirParts == array()) ? array('Matched' => true) : false;
435
		}
436
437
		// Check for the '//' marker that represents the "shifting point"
438
		$doubleSlashPoint = strpos($pattern, '//');
439
		if($doubleSlashPoint !== false) {
440
			$shiftCount = substr_count(substr($pattern,0,$doubleSlashPoint), '/') + 1;
441
			$pattern = str_replace('//', '/', $pattern);
442
			$patternParts = explode('/', $pattern);
443
444
445
		} else {
446
			$patternParts = explode('/', $pattern);
447
			$shiftCount = sizeof($patternParts);
448
		}
449
450
		// Filter out any "empty" matching parts - either from an initial / or a trailing /
451
		$patternParts = array_values(array_filter($patternParts));
452
453
		$arguments = array();
454
		foreach($patternParts as $i => $part) {
455
			$part = trim($part);
456
457
			// Match a variable
458
			if(isset($part[0]) && $part[0] == '$') {
459
				// A variable ending in ! is required
460
				if(substr($part,-1) == '!') {
461
					$varRequired = true;
462
					$varName = substr($part,1,-1);
463
				} else {
464
					$varRequired = false;
465
					$varName = substr($part,1);
466
				}
467
468
				// Fail if a required variable isn't populated
469
				if($varRequired && !isset($this->dirParts[$i])) return false;
470
471
				$arguments[$varName] = isset($this->dirParts[$i]) ? $this->dirParts[$i] : null;
472
				if($part == '$Controller' && (!ClassInfo::exists($arguments['Controller'])
473
						|| !is_subclass_of($arguments['Controller'], 'Controller'))) {
474
475
					return false;
476
				}
477
478
			// Literal parts with extension
479
			} else if(isset($this->dirParts[$i]) && $this->dirParts[$i] . '.' . $this->extension == $part) {
480
				continue;
481
482
			// Literal parts must always be there
483
			} else if(!isset($this->dirParts[$i]) || $this->dirParts[$i] != $part) {
484
				return false;
485
			}
486
487
		}
488
489
		if($shiftOnSuccess) {
490
			$this->shift($shiftCount);
491
			// We keep track of pattern parts that we looked at but didn't shift off.
492
			// This lets us say that we have *parsed* the whole URL even when we haven't *shifted* it all
493
			$this->unshiftedButParsedParts = sizeof($patternParts) - $shiftCount;
0 ignored issues
show
Documentation Bug introduced by
It seems like sizeof($patternParts) - $shiftCount can also be of type double. However, the property $unshiftedButParsedParts is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
494
		}
495
496
		$this->latestParams = $arguments;
497
498
		// Load the arguments that actually have a value into $this->allParams
499
		// This ensures that previous values aren't overridden with blanks
500
		foreach($arguments as $k => $v) {
501
			if($v || !isset($this->allParams[$k])) $this->allParams[$k] = $v;
502
		}
503
504
		if($arguments === array()) $arguments['_matched'] = true;
505
		return $arguments;
506
	}
507
508
	/**
509
	 * @return array
510
	 */
511
	public function allParams() {
512
		return $this->allParams;
513
	}
514
515
	/**
516
	 * Shift all the parameter values down a key space, and return the shifted value.
517
	 *
518
	 * @return string
519
	 */
520
	public function shiftAllParams() {
521
		$keys    = array_keys($this->allParams);
522
		$values  = array_values($this->allParams);
523
		$value   = array_shift($values);
524
525
		// push additional unparsed URL parts onto the parameter stack
526
		if(array_key_exists($this->unshiftedButParsedParts, $this->dirParts)) {
527
			$values[] = $this->dirParts[$this->unshiftedButParsedParts];
528
		}
529
530
		foreach($keys as $position => $key) {
531
			$this->allParams[$key] = isset($values[$position]) ? $values[$position] : null;
532
		}
533
534
		return $value;
535
	}
536
537
	/**
538
	 * @return array
539
	 */
540
	public function latestParams() {
541
		return $this->latestParams;
542
	}
543
544
	/**
545
	 * @param string $name
546
	 * @return string|null
547
	 */
548
	public function latestParam($name) {
549
		if(isset($this->latestParams[$name])) return $this->latestParams[$name];
550
		else return null;
551
	}
552
553
	/**
554
	 * @return array
555
	 */
556
	public function routeParams() {
557
		return $this->routeParams;
558
	}
559
560
	/**
561
	 * @param $params
562
	 * @return SS_HTTPRequest $this
563
	 */
564
	public function setRouteParams($params) {
565
		$this->routeParams = $params;
566
		return $this;
567
	}
568
569
	/**
570
	 * @return array
571
	 */
572
	public function params() {
573
		return array_merge($this->allParams, $this->routeParams);
574
	}
575
576
	/**
577
	 * Finds a named URL parameter (denoted by "$"-prefix in $url_handlers)
578
	 * from the full URL, or a parameter specified in the route table
579
	 *
580
	 * @param string $name
581
	 * @return string Value of the URL parameter (if found)
582
	 */
583
	public function param($name) {
584
		$params = $this->params();
585
		if(isset($params[$name])) return $params[$name];
586
		else return null;
587
	}
588
589
	/**
590
	 * Returns the unparsed part of the original URL
591
	 * separated by commas. This is used by {@link RequestHandler->handleRequest()}
592
	 * to determine if further URL processing is necessary.
593
	 *
594
	 * @return string Partial URL
595
	 */
596
	public function remaining() {
597
		return implode("/", $this->dirParts);
598
	}
599
600
	/**
601
	 * Returns true if this is a URL that will match without shifting off any of the URL.
602
	 * This is used by the request handler to prevent infinite parsing loops.
603
	 *
604
	 * @param $pattern
605
	 * @return bool
606
	 */
607
	public function isEmptyPattern($pattern) {
608
		if(preg_match('/^([A-Za-z]+) +(.*)$/', $pattern, $matches)) {
609
			$pattern = $matches[2];
610
		}
611
612
		if(trim($pattern) == "") return true;
613
	}
614
615
	/**
616
	 * Shift one or more parts off the beginning of the URL.
617
	 * If you specify shifting more than 1 item off, then the items will be returned as an array
618
	 *
619
	 * @param int $count Shift Count
620
	 * @return String|Array
621
	 */
622
	public function shift($count = 1) {
623
		$return = array();
624
625
		if($count == 1) return array_shift($this->dirParts);
626
627
		for($i=0;$i<$count;$i++) {
628
			$value = array_shift($this->dirParts);
629
630
			if($value === null) break;
631
632
			$return[] = $value;
633
		}
634
635
		return $return;
636
	}
637
638
	/**
639
	 * Returns true if the URL has been completely parsed.
640
	 * This will respect parsed but unshifted directory parts.
641
	 *
642
	 * @return bool
643
	 */
644
	public function allParsed() {
645
		return sizeof($this->dirParts) <= $this->unshiftedButParsedParts;
646
	}
647
648
	/**
649
	 * Returns the client IP address which
650
	 * originated this request.
651
	 *
652
	 * @return string
653
	 */
654
	public function getIP() {
655
		$headerOverrideIP = null;
656
		if(TRUSTED_PROXY) {
657
			$headers = (defined('SS_TRUSTED_PROXY_IP_HEADER')) ? array(SS_TRUSTED_PROXY_IP_HEADER) : null;
658
			if(!$headers) {
659
				// Backwards compatible defaults
660
				$headers = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR');
661
			}
662
			foreach($headers as $header) {
663
				if(!empty($_SERVER[$header])) {
664
					$headerOverrideIP = $_SERVER[$header];
665
					break;
666
				}
667
			}
668
		}
669
670
		if ($headerOverrideIP) {
671
			return $this->getIPFromHeaderValue($headerOverrideIP);
672
		} elseif(isset($_SERVER['REMOTE_ADDR'])) {
673
			return $_SERVER['REMOTE_ADDR'];
674
		} else {
675
			return null;
676
		}
677
	}
678
679
	/**
680
	 * Extract an IP address from a header value that has been obtained. Accepts single IP or comma separated string of
681
	 * IPs
682
	 *
683
	 * @param string $headerValue The value from a trusted header
684
	 * @return string The IP address
685
	 */
686
	protected function getIPFromHeaderValue($headerValue) {
687
		if (strpos($headerValue, ',') !== false) {
688
			//sometimes the IP from a load balancer could be "x.x.x.x, y.y.y.y, z.z.z.z" so we need to find the most
689
			// likely candidate
690
			$ips = explode(',', $headerValue);
691
			foreach ($ips as $ip) {
692
				$ip = trim($ip);
693
				if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
694
					return $ip;
695
				}
696
			}
697
		}
698
		return $headerValue;
699
	}
700
701
	/**
702
	 * Returns all mimetypes from the HTTP "Accept" header
703
	 * as an array.
704
	 *
705
	 * @param boolean $includeQuality Don't strip away optional "quality indicators", e.g. "application/xml;q=0.9"
706
	 *                                (Default: false)
707
	 * @return array
708
	 */
709
	public function getAcceptMimetypes($includeQuality = false) {
710
		$mimetypes = array();
711
		$mimetypesWithQuality = explode(',',$this->getHeader('Accept'));
712
		foreach($mimetypesWithQuality as $mimetypeWithQuality) {
713
			$mimetypes[] = ($includeQuality) ? $mimetypeWithQuality : preg_replace('/;.*/', '', $mimetypeWithQuality);
714
		}
715
		return $mimetypes;
716
	}
717
718
	/**
719
	 * @return string HTTP method (all uppercase)
720
	 */
721
	public function httpMethod() {
722
		return $this->httpMethod;
723
	}
724
725
	/**
726
	 * Explicitly set the HTTP method for this request.
727
	 * @param string $method
728
	 * @return $this
729
	 */
730
	public function setHttpMethod($method) {
731
		if(!self::isValidHttpMethod($method)) {
732
			user_error('SS_HTTPRequest::setHttpMethod: Invalid HTTP method', E_USER_ERROR);
733
		}
734
735
		$this->httpMethod = strtoupper($method);
736
		return $this;
737
	}
738
739
	/**
740
	 * @param string $method
741
	 * @return bool
742
	 */
743
	private static function isValidHttpMethod($method) {
744
		return in_array(strtoupper($method), array('GET','POST','PUT','DELETE','HEAD'));
745
	}
746
747
	/**
748
	 * Gets the "real" HTTP method for a request. This method is no longer used to mitigate the risk of web cache
749
	 * poisoning.
750
	 *
751
	 * @see https://www.silverstripe.org/download/security-releases/CVE-2019-19326
752
	 * @param string $origMethod Original HTTP method from the browser request
753
	 * @param array $postVars
754
	 * @return string HTTP method (all uppercase)
755
	 * @deprecated 3.7.5
756
	 */
757
	public static function detect_method($origMethod, $postVars) {
758
		if(isset($postVars['_method'])) {
759
			if (!self::isValidHttpMethod($postVars['_method'])) {
760
				user_error('Director::direct(): Invalid "_method" parameter', E_USER_ERROR);
761
			}
762
			return strtoupper($postVars['_method']);
763
		} else {
764
			return $origMethod;
765
		}
766
	}
767
}
768