Request.php 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. <?php
  2. namespace Illuminate\Http;
  3. use Closure;
  4. use ArrayAccess;
  5. use RuntimeException;
  6. use Illuminate\Support\Arr;
  7. use Illuminate\Support\Str;
  8. use Illuminate\Support\Traits\Macroable;
  9. use Illuminate\Contracts\Support\Arrayable;
  10. use Symfony\Component\HttpFoundation\ParameterBag;
  11. use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
  12. class Request extends SymfonyRequest implements Arrayable, ArrayAccess
  13. {
  14. use Concerns\InteractsWithContentTypes,
  15. Concerns\InteractsWithFlashData,
  16. Concerns\InteractsWithInput,
  17. Macroable;
  18. /**
  19. * The decoded JSON content for the request.
  20. *
  21. * @var \Symfony\Component\HttpFoundation\ParameterBag|null
  22. */
  23. protected $json;
  24. /**
  25. * All of the converted files for the request.
  26. *
  27. * @var array
  28. */
  29. protected $convertedFiles;
  30. /**
  31. * The user resolver callback.
  32. *
  33. * @var \Closure
  34. */
  35. protected $userResolver;
  36. /**
  37. * The route resolver callback.
  38. *
  39. * @var \Closure
  40. */
  41. protected $routeResolver;
  42. /**
  43. * Create a new Illuminate HTTP request from server variables.
  44. *
  45. * @return static
  46. */
  47. public static function capture()
  48. {
  49. static::enableHttpMethodParameterOverride();
  50. return static::createFromBase(SymfonyRequest::createFromGlobals());
  51. }
  52. /**
  53. * Return the Request instance.
  54. *
  55. * @return $this
  56. */
  57. public function instance()
  58. {
  59. return $this;
  60. }
  61. /**
  62. * Get the request method.
  63. *
  64. * @return string
  65. */
  66. public function method()
  67. {
  68. return $this->getMethod();
  69. }
  70. /**
  71. * Get the root URL for the application.
  72. *
  73. * @return string
  74. */
  75. public function root()
  76. {
  77. return rtrim($this->getSchemeAndHttpHost().$this->getBaseUrl(), '/');
  78. }
  79. /**
  80. * Get the URL (no query string) for the request.
  81. *
  82. * @return string
  83. */
  84. public function url()
  85. {
  86. return rtrim(preg_replace('/\?.*/', '', $this->getUri()), '/');
  87. }
  88. /**
  89. * Get the full URL for the request.
  90. *
  91. * @return string
  92. */
  93. public function fullUrl()
  94. {
  95. $query = $this->getQueryString();
  96. $question = $this->getBaseUrl().$this->getPathInfo() == '/' ? '/?' : '?';
  97. return $query ? $this->url().$question.$query : $this->url();
  98. }
  99. /**
  100. * Get the full URL for the request with the added query string parameters.
  101. *
  102. * @param array $query
  103. * @return string
  104. */
  105. public function fullUrlWithQuery(array $query)
  106. {
  107. $question = $this->getBaseUrl().$this->getPathInfo() == '/' ? '/?' : '?';
  108. return count($this->query()) > 0
  109. ? $this->url().$question.http_build_query(array_merge($this->query(), $query))
  110. : $this->fullUrl().$question.http_build_query($query);
  111. }
  112. /**
  113. * Get the current path info for the request.
  114. *
  115. * @return string
  116. */
  117. public function path()
  118. {
  119. $pattern = trim($this->getPathInfo(), '/');
  120. return $pattern == '' ? '/' : $pattern;
  121. }
  122. /**
  123. * Get the current decoded path info for the request.
  124. *
  125. * @return string
  126. */
  127. public function decodedPath()
  128. {
  129. return rawurldecode($this->path());
  130. }
  131. /**
  132. * Get a segment from the URI (1 based index).
  133. *
  134. * @param int $index
  135. * @param string|null $default
  136. * @return string|null
  137. */
  138. public function segment($index, $default = null)
  139. {
  140. return Arr::get($this->segments(), $index - 1, $default);
  141. }
  142. /**
  143. * Get all of the segments for the request path.
  144. *
  145. * @return array
  146. */
  147. public function segments()
  148. {
  149. $segments = explode('/', $this->decodedPath());
  150. return array_values(array_filter($segments, function ($value) {
  151. return $value !== '';
  152. }));
  153. }
  154. /**
  155. * Determine if the current request URI matches a pattern.
  156. *
  157. * @param dynamic $patterns
  158. * @return bool
  159. */
  160. public function is(...$patterns)
  161. {
  162. foreach ($patterns as $pattern) {
  163. if (Str::is($pattern, $this->decodedPath())) {
  164. return true;
  165. }
  166. }
  167. return false;
  168. }
  169. /**
  170. * Determine if the route name matches a given pattern.
  171. *
  172. * @param dynamic $patterns
  173. * @return bool
  174. */
  175. public function routeIs(...$patterns)
  176. {
  177. return $this->route() && $this->route()->named(...$patterns);
  178. }
  179. /**
  180. * Determine if the current request URL and query string matches a pattern.
  181. *
  182. * @param dynamic $patterns
  183. * @return bool
  184. */
  185. public function fullUrlIs(...$patterns)
  186. {
  187. $url = $this->fullUrl();
  188. foreach ($patterns as $pattern) {
  189. if (Str::is($pattern, $url)) {
  190. return true;
  191. }
  192. }
  193. return false;
  194. }
  195. /**
  196. * Determine if the request is the result of an AJAX call.
  197. *
  198. * @return bool
  199. */
  200. public function ajax()
  201. {
  202. return $this->isXmlHttpRequest();
  203. }
  204. /**
  205. * Determine if the request is the result of an PJAX call.
  206. *
  207. * @return bool
  208. */
  209. public function pjax()
  210. {
  211. return $this->headers->get('X-PJAX') == true;
  212. }
  213. /**
  214. * Determine if the request is over HTTPS.
  215. *
  216. * @return bool
  217. */
  218. public function secure()
  219. {
  220. return $this->isSecure();
  221. }
  222. /**
  223. * Get the client IP address.
  224. *
  225. * @return string
  226. */
  227. public function ip()
  228. {
  229. return $this->getClientIp();
  230. }
  231. /**
  232. * Get the client IP addresses.
  233. *
  234. * @return array
  235. */
  236. public function ips()
  237. {
  238. return $this->getClientIps();
  239. }
  240. /**
  241. * Get the client user agent.
  242. *
  243. * @return string
  244. */
  245. public function userAgent()
  246. {
  247. return $this->headers->get('User-Agent');
  248. }
  249. /**
  250. * Merge new input into the current request's input array.
  251. *
  252. * @param array $input
  253. * @return \Illuminate\Http\Request
  254. */
  255. public function merge(array $input)
  256. {
  257. $this->getInputSource()->add($input);
  258. return $this;
  259. }
  260. /**
  261. * Replace the input for the current request.
  262. *
  263. * @param array $input
  264. * @return \Illuminate\Http\Request
  265. */
  266. public function replace(array $input)
  267. {
  268. $this->getInputSource()->replace($input);
  269. return $this;
  270. }
  271. /**
  272. * Get the JSON payload for the request.
  273. *
  274. * @param string $key
  275. * @param mixed $default
  276. * @return \Symfony\Component\HttpFoundation\ParameterBag|mixed
  277. */
  278. public function json($key = null, $default = null)
  279. {
  280. if (! isset($this->json)) {
  281. $this->json = new ParameterBag((array) json_decode($this->getContent(), true));
  282. }
  283. if (is_null($key)) {
  284. return $this->json;
  285. }
  286. return data_get($this->json->all(), $key, $default);
  287. }
  288. /**
  289. * Get the input source for the request.
  290. *
  291. * @return \Symfony\Component\HttpFoundation\ParameterBag
  292. */
  293. protected function getInputSource()
  294. {
  295. if ($this->isJson()) {
  296. return $this->json();
  297. }
  298. return in_array($this->getRealMethod(), ['GET', 'HEAD']) ? $this->query : $this->request;
  299. }
  300. /**
  301. * Create a new request instance from the given Laravel request.
  302. *
  303. * @param \Illuminate\Http\Request $from
  304. * @param \Illuminate\Http\Request|null $to
  305. * @return static
  306. */
  307. public static function createFrom(self $from, $to = null)
  308. {
  309. $request = $to ?: new static;
  310. $files = $from->files->all();
  311. $files = is_array($files) ? array_filter($files) : $files;
  312. $request->initialize(
  313. $from->query->all(),
  314. $from->request->all(),
  315. $from->attributes->all(),
  316. $from->cookies->all(),
  317. $files,
  318. $from->server->all(),
  319. $from->getContent()
  320. );
  321. $request->setJson($from->json());
  322. if ($session = $from->getSession()) {
  323. $request->setLaravelSession($session);
  324. }
  325. $request->setUserResolver($from->getUserResolver());
  326. $request->setRouteResolver($from->getRouteResolver());
  327. return $request;
  328. }
  329. /**
  330. * Create an Illuminate request from a Symfony instance.
  331. *
  332. * @param \Symfony\Component\HttpFoundation\Request $request
  333. * @return \Illuminate\Http\Request
  334. */
  335. public static function createFromBase(SymfonyRequest $request)
  336. {
  337. if ($request instanceof static) {
  338. return $request;
  339. }
  340. $content = $request->content;
  341. $request = (new static)->duplicate(
  342. $request->query->all(), $request->request->all(), $request->attributes->all(),
  343. $request->cookies->all(), $request->files->all(), $request->server->all()
  344. );
  345. $request->content = $content;
  346. $request->request = $request->getInputSource();
  347. return $request;
  348. }
  349. /**
  350. * {@inheritdoc}
  351. */
  352. public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
  353. {
  354. return parent::duplicate($query, $request, $attributes, $cookies, $this->filterFiles($files), $server);
  355. }
  356. /**
  357. * Filter the given array of files, removing any empty values.
  358. *
  359. * @param mixed $files
  360. * @return mixed
  361. */
  362. protected function filterFiles($files)
  363. {
  364. if (! $files) {
  365. return;
  366. }
  367. foreach ($files as $key => $file) {
  368. if (is_array($file)) {
  369. $files[$key] = $this->filterFiles($files[$key]);
  370. }
  371. if (empty($files[$key])) {
  372. unset($files[$key]);
  373. }
  374. }
  375. return $files;
  376. }
  377. /**
  378. * Get the session associated with the request.
  379. *
  380. * @return \Illuminate\Session\Store
  381. *
  382. * @throws \RuntimeException
  383. */
  384. public function session()
  385. {
  386. if (! $this->hasSession()) {
  387. throw new RuntimeException('Session store not set on request.');
  388. }
  389. return $this->session;
  390. }
  391. /**
  392. * Get the session associated with the request.
  393. *
  394. * @return \Illuminate\Session\Store|null
  395. */
  396. public function getSession()
  397. {
  398. return $this->session;
  399. }
  400. /**
  401. * Set the session instance on the request.
  402. *
  403. * @param \Illuminate\Contracts\Session\Session $session
  404. * @return void
  405. */
  406. public function setLaravelSession($session)
  407. {
  408. $this->session = $session;
  409. }
  410. /**
  411. * Get the user making the request.
  412. *
  413. * @param string|null $guard
  414. * @return mixed
  415. */
  416. public function user($guard = null)
  417. {
  418. return call_user_func($this->getUserResolver(), $guard);
  419. }
  420. /**
  421. * Get the route handling the request.
  422. *
  423. * @param string|null $param
  424. *
  425. * @return \Illuminate\Routing\Route|object|string
  426. */
  427. public function route($param = null)
  428. {
  429. $route = call_user_func($this->getRouteResolver());
  430. if (is_null($route) || is_null($param)) {
  431. return $route;
  432. }
  433. return $route->parameter($param);
  434. }
  435. /**
  436. * Get a unique fingerprint for the request / route / IP address.
  437. *
  438. * @return string
  439. *
  440. * @throws \RuntimeException
  441. */
  442. public function fingerprint()
  443. {
  444. if (! $route = $this->route()) {
  445. throw new RuntimeException('Unable to generate fingerprint. Route unavailable.');
  446. }
  447. return sha1(implode('|', array_merge(
  448. $route->methods(),
  449. [$route->getDomain(), $route->uri(), $this->ip()]
  450. )));
  451. }
  452. /**
  453. * Set the JSON payload for the request.
  454. *
  455. * @param \Symfony\Component\HttpFoundation\ParameterBag $json
  456. * @return $this
  457. */
  458. public function setJson($json)
  459. {
  460. $this->json = $json;
  461. return $this;
  462. }
  463. /**
  464. * Get the user resolver callback.
  465. *
  466. * @return \Closure
  467. */
  468. public function getUserResolver()
  469. {
  470. return $this->userResolver ?: function () {
  471. //
  472. };
  473. }
  474. /**
  475. * Set the user resolver callback.
  476. *
  477. * @param \Closure $callback
  478. * @return $this
  479. */
  480. public function setUserResolver(Closure $callback)
  481. {
  482. $this->userResolver = $callback;
  483. return $this;
  484. }
  485. /**
  486. * Get the route resolver callback.
  487. *
  488. * @return \Closure
  489. */
  490. public function getRouteResolver()
  491. {
  492. return $this->routeResolver ?: function () {
  493. //
  494. };
  495. }
  496. /**
  497. * Set the route resolver callback.
  498. *
  499. * @param \Closure $callback
  500. * @return $this
  501. */
  502. public function setRouteResolver(Closure $callback)
  503. {
  504. $this->routeResolver = $callback;
  505. return $this;
  506. }
  507. /**
  508. * Get all of the input and files for the request.
  509. *
  510. * @return array
  511. */
  512. public function toArray()
  513. {
  514. return $this->all();
  515. }
  516. /**
  517. * Determine if the given offset exists.
  518. *
  519. * @param string $offset
  520. * @return bool
  521. */
  522. public function offsetExists($offset)
  523. {
  524. return array_key_exists(
  525. $offset,
  526. $this->all() + $this->route()->parameters()
  527. );
  528. }
  529. /**
  530. * Get the value at the given offset.
  531. *
  532. * @param string $offset
  533. * @return mixed
  534. */
  535. public function offsetGet($offset)
  536. {
  537. return $this->__get($offset);
  538. }
  539. /**
  540. * Set the value at the given offset.
  541. *
  542. * @param string $offset
  543. * @param mixed $value
  544. * @return void
  545. */
  546. public function offsetSet($offset, $value)
  547. {
  548. $this->getInputSource()->set($offset, $value);
  549. }
  550. /**
  551. * Remove the value at the given offset.
  552. *
  553. * @param string $offset
  554. * @return void
  555. */
  556. public function offsetUnset($offset)
  557. {
  558. $this->getInputSource()->remove($offset);
  559. }
  560. /**
  561. * Check if an input element is set on the request.
  562. *
  563. * @param string $key
  564. * @return bool
  565. */
  566. public function __isset($key)
  567. {
  568. return ! is_null($this->__get($key));
  569. }
  570. /**
  571. * Get an input element from the request.
  572. *
  573. * @param string $key
  574. * @return mixed
  575. */
  576. public function __get($key)
  577. {
  578. if (array_key_exists($key, $this->all())) {
  579. return data_get($this->all(), $key);
  580. }
  581. return $this->route($key);
  582. }
  583. }