Request.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\HTTP\PhpEnvironment;
  7. use Magento\Framework\Stdlib\Cookie\CookieReaderInterface;
  8. use Magento\Framework\Stdlib\StringUtils;
  9. use Zend\Http\Header\HeaderInterface;
  10. use Zend\Stdlib\Parameters;
  11. use Zend\Stdlib\ParametersInterface;
  12. use Zend\Uri\UriFactory;
  13. use Zend\Uri\UriInterface;
  14. /**
  15. * HTTP Request for current PHP environment.
  16. *
  17. * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
  18. * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
  19. */
  20. class Request extends \Zend\Http\PhpEnvironment\Request
  21. {
  22. /**#@+
  23. * Protocols
  24. */
  25. const SCHEME_HTTP = 'http';
  26. const SCHEME_HTTPS = 'https';
  27. /**#@-*/
  28. // Configuration path for SSL Offload http header
  29. const XML_PATH_OFFLOADER_HEADER = 'web/secure/offloader_header';
  30. /**
  31. * @var string
  32. */
  33. protected $module;
  34. /**
  35. * @var string
  36. */
  37. protected $controller;
  38. /**
  39. * @var string
  40. */
  41. protected $action;
  42. /**
  43. * PATH_INFO
  44. *
  45. * @var string
  46. */
  47. protected $pathInfo = '';
  48. /**
  49. * @var string
  50. */
  51. protected $requestString = '';
  52. /**
  53. * Request parameters
  54. *
  55. * @var array
  56. */
  57. protected $params = [];
  58. /**
  59. * @var array
  60. */
  61. protected $aliases = [];
  62. /**
  63. * Has the action been dispatched?
  64. *
  65. * @var boolean
  66. */
  67. protected $dispatched = false;
  68. /**
  69. * Flag for whether the request is forwarded or not
  70. *
  71. * @var bool
  72. */
  73. protected $forwarded;
  74. /**
  75. * @var CookieReaderInterface
  76. */
  77. protected $cookieReader;
  78. /**
  79. * @var StringUtils
  80. */
  81. protected $converter;
  82. /**
  83. * @var \Magento\Framework\App\Config
  84. */
  85. protected $appConfig;
  86. /**
  87. * Name of http header to check for ssl offloading default value is X-Forwarded-Proto
  88. *
  89. * @var string
  90. */
  91. protected $sslOffloadHeader;
  92. /**
  93. * @param CookieReaderInterface $cookieReader
  94. * @param StringUtils $converter
  95. * @param UriInterface|string|null $uri
  96. */
  97. public function __construct(
  98. CookieReaderInterface $cookieReader,
  99. StringUtils $converter,
  100. $uri = null
  101. ) {
  102. $this->cookieReader = $cookieReader;
  103. if (null !== $uri) {
  104. if (!$uri instanceof UriInterface) {
  105. $uri = UriFactory::factory($uri);
  106. }
  107. if ($uri->isValid()) {
  108. $path = $uri->getPath();
  109. $query = $uri->getQuery();
  110. if (!empty($query)) {
  111. $path .= '?' . $query;
  112. }
  113. $this->setRequestUri($path);
  114. } else {
  115. throw new \InvalidArgumentException('Invalid URI provided to constructor');
  116. }
  117. }
  118. $this->converter = $converter;
  119. parent::__construct();
  120. }
  121. /**
  122. * Retrieve the module name
  123. *
  124. * @return string
  125. */
  126. public function getModuleName()
  127. {
  128. return $this->module;
  129. }
  130. /**
  131. * Set the module name to use
  132. *
  133. * @param string $value
  134. * @return $this
  135. */
  136. public function setModuleName($value)
  137. {
  138. $this->module = $value;
  139. return $this;
  140. }
  141. /**
  142. * Retrieve the controller name
  143. *
  144. * @return string
  145. */
  146. public function getControllerName()
  147. {
  148. return $this->controller;
  149. }
  150. /**
  151. * Set the controller name to use
  152. *
  153. * @param string $value
  154. * @return $this
  155. */
  156. public function setControllerName($value)
  157. {
  158. $this->controller = $value;
  159. return $this;
  160. }
  161. /**
  162. * Retrieve the action name
  163. *
  164. * @return string
  165. */
  166. public function getActionName()
  167. {
  168. return $this->action;
  169. }
  170. /**
  171. * Set the action name
  172. *
  173. * @param string $value
  174. * @return $this
  175. */
  176. public function setActionName($value)
  177. {
  178. $this->action = $value;
  179. return $this;
  180. }
  181. /**
  182. * Returns everything between the BaseUrl and QueryString.
  183. * This value is calculated instead of reading PATH_INFO
  184. * directly from $_SERVER due to cross-platform differences.
  185. *
  186. * @return string
  187. */
  188. public function getPathInfo()
  189. {
  190. if (empty($this->pathInfo)) {
  191. $this->setPathInfo();
  192. }
  193. return $this->pathInfo;
  194. }
  195. /**
  196. * Set the PATH_INFO string
  197. *
  198. * @param string|null $pathInfo
  199. * @return $this
  200. */
  201. public function setPathInfo($pathInfo = null)
  202. {
  203. if ($pathInfo === null) {
  204. $requestUri = $this->getRequestUri();
  205. if ('/' == $requestUri) {
  206. return $this;
  207. }
  208. // Remove the query string from REQUEST_URI
  209. $pos = strpos($requestUri, '?');
  210. if ($pos) {
  211. $requestUri = substr($requestUri, 0, $pos);
  212. }
  213. $baseUrl = $this->getBaseUrl();
  214. $pathInfo = substr($requestUri, strlen($baseUrl));
  215. if (!empty($baseUrl) && '/' === $pathInfo) {
  216. $pathInfo = '';
  217. } elseif (null === $baseUrl) {
  218. $pathInfo = $requestUri;
  219. }
  220. $this->requestString = $pathInfo . ($pos !== false ? substr($requestUri, $pos) : '');
  221. }
  222. $this->pathInfo = (string)$pathInfo;
  223. return $this;
  224. }
  225. /**
  226. * Get request string
  227. *
  228. * @return string
  229. */
  230. public function getRequestString()
  231. {
  232. return $this->requestString;
  233. }
  234. /**
  235. * Retrieve an alias
  236. *
  237. * Retrieve the actual key represented by the alias $name.
  238. *
  239. * @param string $name
  240. * @return string|null Returns null when no alias exists
  241. */
  242. public function getAlias($name)
  243. {
  244. if (isset($this->aliases[$name])) {
  245. return $this->aliases[$name];
  246. }
  247. return null;
  248. }
  249. /**
  250. * Set a key alias
  251. *
  252. * Set an alias used for key lookups. $name specifies the alias, $target
  253. * specifies the actual key to use.
  254. *
  255. * @param string $name
  256. * @param string $target
  257. * @return $this
  258. */
  259. public function setAlias($name, $target)
  260. {
  261. $this->aliases[$name] = $target;
  262. return $this;
  263. }
  264. /**
  265. * Get an action parameter
  266. *
  267. * @param string $key
  268. * @param mixed $default Default value to use if key not found
  269. * @return mixed
  270. */
  271. public function getParam($key, $default = null)
  272. {
  273. $key = (string) $key;
  274. $keyName = (null !== ($alias = $this->getAlias($key))) ? $alias : $key;
  275. if (isset($this->params[$keyName])) {
  276. return $this->params[$keyName];
  277. } elseif (isset($this->queryParams[$keyName])) {
  278. return $this->queryParams[$keyName];
  279. } elseif (isset($this->postParams[$keyName])) {
  280. return $this->postParams[$keyName];
  281. }
  282. return $default;
  283. }
  284. /**
  285. * Set an action parameter
  286. *
  287. * A $value of null will unset the $key if it exists
  288. *
  289. * @param string $key
  290. * @param mixed $value
  291. * @return $this
  292. */
  293. public function setParam($key, $value)
  294. {
  295. $key = (string) $key;
  296. $keyName = (null !== ($alias = $this->getAlias($key))) ? $alias : $key;
  297. if ((null === $value) && isset($this->params[$keyName])) {
  298. unset($this->params[$keyName]);
  299. } elseif (null !== $value) {
  300. $this->params[$keyName] = $value;
  301. }
  302. return $this;
  303. }
  304. /**
  305. * Get all action parameters
  306. *
  307. * @return array
  308. */
  309. public function getParams()
  310. {
  311. $params = $this->params;
  312. if ($value = (array)$this->getQuery()) {
  313. $params += $value;
  314. }
  315. if ($value = (array)$this->getPost()) {
  316. $params += $value;
  317. }
  318. return $params;
  319. }
  320. /**
  321. * Set action parameters en masse; does not overwrite
  322. *
  323. * Null values will unset the associated key.
  324. *
  325. * @param array $array
  326. * @return $this
  327. */
  328. public function setParams(array $array)
  329. {
  330. foreach ($array as $key => $value) {
  331. $this->setParam($key, $value);
  332. }
  333. return $this;
  334. }
  335. /**
  336. * Unset all user parameters
  337. *
  338. * @return $this
  339. */
  340. public function clearParams()
  341. {
  342. $this->params = [];
  343. return $this;
  344. }
  345. /**
  346. * Get the request URI scheme
  347. *
  348. * @return string
  349. */
  350. public function getScheme()
  351. {
  352. return $this->isSecure() ? self::SCHEME_HTTPS : self::SCHEME_HTTP;
  353. }
  354. /**
  355. * Set flag indicating whether or not request has been dispatched
  356. *
  357. * @param boolean $flag
  358. * @return $this
  359. */
  360. public function setDispatched($flag = true)
  361. {
  362. $this->dispatched = $flag ? true : false;
  363. return $this;
  364. }
  365. /**
  366. * Determine if the request has been dispatched
  367. *
  368. * @return boolean
  369. */
  370. public function isDispatched()
  371. {
  372. return $this->dispatched;
  373. }
  374. /**
  375. * Is https secure request
  376. *
  377. * @return bool
  378. */
  379. public function isSecure()
  380. {
  381. if ($this->immediateRequestSecure()) {
  382. return true;
  383. }
  384. return $this->initialRequestSecure($this->getSslOffloadHeader());
  385. }
  386. /**
  387. * Get value of SSL offload http header from configuration - defaults to X-Forwarded-Proto
  388. *
  389. * @return string
  390. */
  391. private function getSslOffloadHeader()
  392. {
  393. // Lets read from db only one time okay.
  394. if ($this->sslOffloadHeader === null) {
  395. // @todo: Untangle Config dependence on Scope, so that this class can be instantiated even if app is not
  396. // installed MAGETWO-31756
  397. // Check if a proxy sent a header indicating an initial secure request
  398. $this->sslOffloadHeader = trim(
  399. (string)$this->getAppConfig()->getValue(
  400. self::XML_PATH_OFFLOADER_HEADER,
  401. \Magento\Framework\App\Config\ScopeConfigInterface::SCOPE_TYPE_DEFAULT
  402. )
  403. );
  404. }
  405. return $this->sslOffloadHeader;
  406. }
  407. /**
  408. * Create an instance of Magento\Framework\App\Config
  409. *
  410. * @return \Magento\Framework\App\Config
  411. * @deprecated 100.1.0
  412. */
  413. private function getAppConfig()
  414. {
  415. if ($this->appConfig == null) {
  416. $this->appConfig =
  417. \Magento\Framework\App\ObjectManager::getInstance()->get(\Magento\Framework\App\Config::class);
  418. }
  419. return $this->appConfig;
  420. }
  421. /**
  422. * Checks if the immediate request is delivered over HTTPS
  423. *
  424. * @return bool
  425. */
  426. protected function immediateRequestSecure()
  427. {
  428. $https = $this->getServer('HTTPS');
  429. $headerServerPort = $this->getServer('SERVER_PORT');
  430. return (!empty($https) && $https != 'off') || $headerServerPort == 443;
  431. }
  432. /**
  433. * In case there is a proxy server, checks if the initial request to the proxy was delivered over HTTPS
  434. *
  435. * @param string $offLoaderHeader
  436. * @return bool
  437. */
  438. protected function initialRequestSecure($offLoaderHeader)
  439. {
  440. // Transform http header to $_SERVER format ie X-Forwarded-Proto becomes $_SERVER['HTTP_X_FORWARDED_PROTO']
  441. $offLoaderHeader = str_replace('-', '_', strtoupper($offLoaderHeader));
  442. // Some webservers do not append HTTP_
  443. $header = $this->getServer($offLoaderHeader);
  444. // Apache appends HTTP_
  445. $httpHeader = $this->getServer('HTTP_' . $offLoaderHeader);
  446. return !empty($offLoaderHeader) && ($header === 'https' || $httpHeader === 'https');
  447. }
  448. /**
  449. * Retrieve a value from a cookie.
  450. *
  451. * @param string|null $name
  452. * @param string|null $default The default value to return if no value could be found for the given $name.
  453. * @return string|null
  454. */
  455. public function getCookie($name = null, $default = null)
  456. {
  457. return $this->cookieReader->getCookie($name, $default);
  458. }
  459. /**
  460. * Retrieve SERVER parameters
  461. *
  462. * @param string $name
  463. * @param mixed $default
  464. * @return mixed|ParametersInterface
  465. */
  466. public function getServerValue($name = null, $default = null)
  467. {
  468. $server = $this->getServer($name, $default);
  469. if ($server instanceof ParametersInterface) {
  470. return $server->toArray();
  471. }
  472. return $server;
  473. }
  474. /**
  475. * Retrieve GET parameters
  476. *
  477. * @param string $name
  478. * @param mixed $default
  479. * @return mixed|ParametersInterface
  480. */
  481. public function getQueryValue($name = null, $default = null)
  482. {
  483. $query = $this->getQuery($name, $default);
  484. if ($query instanceof ParametersInterface) {
  485. return $query->toArray();
  486. }
  487. return $query;
  488. }
  489. /**
  490. * Set GET parameters
  491. *
  492. * @param string $name
  493. * @param mixed $value
  494. * @return $this
  495. */
  496. public function setQueryValue($name, $value = null)
  497. {
  498. if (is_array($name)) {
  499. foreach ($name as $key => $value) {
  500. $this->getQuery()->set($key, $value);
  501. }
  502. return $this;
  503. }
  504. $this->getQuery()->set($name, $value);
  505. return $this;
  506. }
  507. /**
  508. * Retrieve POST parameters
  509. *
  510. * @param string $name
  511. * @param mixed $default
  512. * @return mixed|ParametersInterface
  513. */
  514. public function getPostValue($name = null, $default = null)
  515. {
  516. $post = $this->getPost($name, $default);
  517. if ($post instanceof ParametersInterface) {
  518. return $post->toArray();
  519. }
  520. return $post;
  521. }
  522. /**
  523. * Set POST parameters
  524. *
  525. * @param string|array $name
  526. * @param mixed $value
  527. * @return $this
  528. */
  529. public function setPostValue($name, $value = null)
  530. {
  531. if (is_array($name)) {
  532. $this->setPost(new Parameters($name));
  533. return $this;
  534. }
  535. $this->getPost()->set($name, $value);
  536. return $this;
  537. }
  538. /**
  539. * Access values contained in the superglobals as public members
  540. *
  541. * Order of precedence: 1. GET, 2. POST, 3. COOKIE, 4. SERVER, 5. ENV
  542. *
  543. * @see http://msdn.microsoft.com/en-us/library/system.web.httprequest.item.aspx
  544. * @param string $key
  545. * @return mixed
  546. */
  547. public function __get($key)
  548. {
  549. switch (true) {
  550. case isset($this->params[$key]):
  551. return $this->params[$key];
  552. case isset($this->queryParams[$key]):
  553. return $this->queryParams[$key];
  554. case isset($this->postParams[$key]):
  555. return $this->postParams[$key];
  556. case isset($_COOKIE[$key]):
  557. return $_COOKIE[$key];
  558. case ($key == 'REQUEST_URI'):
  559. return $this->getRequestUri();
  560. case ($key == 'PATH_INFO'):
  561. return $this->getPathInfo();
  562. case isset($this->serverParams[$key]):
  563. return $this->serverParams[$key];
  564. case isset($this->envParams[$key]):
  565. return $this->envParams[$key];
  566. default:
  567. return null;
  568. }
  569. }
  570. /**
  571. * Alias to __get
  572. *
  573. * @param string $key
  574. * @return mixed
  575. */
  576. public function get($key)
  577. {
  578. return $this->__get($key);
  579. }
  580. /**
  581. * Check to see if a property is set
  582. *
  583. * @param string $key
  584. * @return boolean
  585. */
  586. public function __isset($key)
  587. {
  588. switch (true) {
  589. case isset($this->params[$key]):
  590. return true;
  591. case isset($this->queryParams[$key]):
  592. return true;
  593. case isset($this->postParams[$key]):
  594. return true;
  595. case isset($_COOKIE[$key]):
  596. return true;
  597. case isset($this->serverParams[$key]):
  598. return true;
  599. case isset($this->envParams[$key]):
  600. return true;
  601. default:
  602. return false;
  603. }
  604. }
  605. /**
  606. * Alias to __isset()
  607. *
  608. * @param string $key
  609. * @return boolean
  610. */
  611. public function has($key)
  612. {
  613. return $this->__isset($key);
  614. }
  615. /**
  616. * Get all headers of a certain name/type.
  617. *
  618. * @param string $name Header name to retrieve.
  619. * @param mixed|null $default Default value to use when the requested header is missing.
  620. * @return bool|string
  621. */
  622. public function getHeader($name, $default = false)
  623. {
  624. $header = parent::getHeader($name, $default);
  625. if ($header instanceof HeaderInterface) {
  626. return $header->getFieldValue();
  627. }
  628. return false;
  629. }
  630. /**
  631. * Retrieve HTTP HOST
  632. *
  633. * @param bool $trimPort
  634. * @return string
  635. *
  636. * @todo getHttpHost should return only string (currently method return boolean value too)
  637. */
  638. public function getHttpHost($trimPort = true)
  639. {
  640. $httpHost = $this->getServer('HTTP_HOST');
  641. $httpHost = $this->converter->cleanString($httpHost);
  642. if (empty($httpHost)) {
  643. return false;
  644. }
  645. if ($trimPort) {
  646. $host = explode(':', $httpHost);
  647. return $host[0];
  648. }
  649. return $httpHost;
  650. }
  651. /**
  652. * Get the client's IP addres
  653. *
  654. * @param boolean $checkProxy
  655. * @return string
  656. */
  657. public function getClientIp($checkProxy = true)
  658. {
  659. if ($checkProxy && $this->getServer('HTTP_CLIENT_IP') != null) {
  660. $ip = $this->getServer('HTTP_CLIENT_IP');
  661. } elseif ($checkProxy && $this->getServer('HTTP_X_FORWARDED_FOR') != null) {
  662. $ip = $this->getServer('HTTP_X_FORWARDED_FOR');
  663. } else {
  664. $ip = $this->getServer('REMOTE_ADDR');
  665. }
  666. return $ip;
  667. }
  668. /**
  669. * Retrieve only user params
  670. *
  671. * @return array
  672. */
  673. public function getUserParams()
  674. {
  675. return $this->params;
  676. }
  677. /**
  678. * Retrieve a single user param
  679. *
  680. * @param string $key
  681. * @param string $default Default value to use if key not found
  682. * @return mixed
  683. */
  684. public function getUserParam($key, $default = null)
  685. {
  686. if (isset($this->params[$key])) {
  687. return $this->params[$key];
  688. }
  689. return $default;
  690. }
  691. /**
  692. * Set the REQUEST_URI
  693. *
  694. * @param string $requestUri
  695. * @return $this
  696. */
  697. public function setRequestUri($requestUri = null)
  698. {
  699. if ($requestUri === null) {
  700. $requestUri = $this->detectRequestUri();
  701. } elseif (!is_string($requestUri)) {
  702. return $this;
  703. } else {
  704. if (false !== ($pos = strpos($requestUri, '?'))) {
  705. $query = substr($requestUri, $pos + 1);
  706. parse_str($query, $vars);
  707. $this->setQueryValue($vars);
  708. }
  709. }
  710. $this->requestUri = $requestUri;
  711. return $this;
  712. }
  713. /**
  714. * Get base url
  715. *
  716. * @return string
  717. */
  718. public function getBaseUrl()
  719. {
  720. $url = urldecode(parent::getBaseUrl());
  721. $url = str_replace('\\', '/', $url);
  722. return $url;
  723. }
  724. /**
  725. * Get flag value for whether the request is forwarded or not.
  726. *
  727. * @return bool
  728. * @codeCoverageIgnore
  729. */
  730. public function isForwarded()
  731. {
  732. return $this->forwarded;
  733. }
  734. /**
  735. * Set flag value for whether the request is forwarded or not.
  736. *
  737. * @param bool $forwarded
  738. * @return $this
  739. * @codeCoverageIgnore
  740. */
  741. public function setForwarded($forwarded)
  742. {
  743. $this->forwarded = $forwarded;
  744. return $this;
  745. }
  746. }