ServerRequestFactory.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.3.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Http;
  16. use Cake\Core\Configure;
  17. use Cake\Utility\Hash;
  18. use Zend\Diactoros\ServerRequestFactory as BaseFactory;
  19. /**
  20. * Factory for making ServerRequest instances.
  21. *
  22. * This subclass adds in CakePHP specific behavior to populate
  23. * the basePath and webroot attributes. Furthermore the Uri's path
  24. * is corrected to only contain the 'virtual' path for the request.
  25. */
  26. abstract class ServerRequestFactory extends BaseFactory
  27. {
  28. /**
  29. * {@inheritDoc}
  30. */
  31. public static function fromGlobals(
  32. array $server = null,
  33. array $query = null,
  34. array $body = null,
  35. array $cookies = null,
  36. array $files = null
  37. ) {
  38. $server = static::normalizeServer($server ?: $_SERVER);
  39. $uri = static::createUri($server);
  40. $sessionConfig = (array)Configure::read('Session') + [
  41. 'defaults' => 'php',
  42. 'cookiePath' => $uri->webroot
  43. ];
  44. $session = Session::create($sessionConfig);
  45. $request = new ServerRequest([
  46. 'environment' => $server,
  47. 'uri' => $uri,
  48. 'files' => $files ?: $_FILES,
  49. 'cookies' => $cookies ?: $_COOKIE,
  50. 'query' => $query ?: $_GET,
  51. 'post' => $body ?: $_POST,
  52. 'webroot' => $uri->webroot,
  53. 'base' => $uri->base,
  54. 'session' => $session,
  55. ]);
  56. return $request;
  57. }
  58. /**
  59. * Create a new Uri instance from the provided server data.
  60. *
  61. * @param array $server Array of server data to build the Uri from.
  62. * $_SERVER will be added into the $server parameter.
  63. * @return \Psr\Http\Message\UriInterface New instance.
  64. */
  65. public static function createUri(array $server = [])
  66. {
  67. $server += $_SERVER;
  68. $server = static::normalizeServer($server);
  69. $headers = static::marshalHeaders($server);
  70. return static::marshalUriFromServer($server, $headers);
  71. }
  72. /**
  73. * Build a UriInterface object.
  74. *
  75. * Add in some CakePHP specific logic/properties that help
  76. * perserve backwards compatibility.
  77. *
  78. * @param array $server The server parameters.
  79. * @param array $headers The normalized headers
  80. * @return \Psr\Http\Message\UriInterface a constructed Uri
  81. */
  82. public static function marshalUriFromServer(array $server, array $headers)
  83. {
  84. $uri = parent::marshalUriFromServer($server, $headers);
  85. list($base, $webroot) = static::getBase($uri, $server);
  86. // Look in PATH_INFO first, as this is the exact value we need prepared
  87. // by PHP.
  88. $pathInfo = Hash::get($server, 'PATH_INFO');
  89. if ($pathInfo) {
  90. $uri = $uri->withPath($pathInfo);
  91. } else {
  92. $uri = static::updatePath($base, $uri);
  93. }
  94. if (!$uri->getHost()) {
  95. $uri = $uri->withHost('localhost');
  96. }
  97. // Splat on some extra attributes to save
  98. // some method calls.
  99. $uri->base = $base;
  100. $uri->webroot = $webroot;
  101. return $uri;
  102. }
  103. /**
  104. * Updates the request URI to remove the base directory.
  105. *
  106. * @param string $base The base path to remove.
  107. * @param \Psr\Http\Message\UriInterface $uri The uri to update.
  108. * @return \Psr\Http\Message\UriInterface The modified Uri instance.
  109. */
  110. protected static function updatePath($base, $uri)
  111. {
  112. $path = $uri->getPath();
  113. if (strlen($base) > 0 && strpos($path, $base) === 0) {
  114. $path = substr($path, strlen($base));
  115. }
  116. if ($path === '/index.php' && $uri->getQuery()) {
  117. $path = $uri->getQuery();
  118. }
  119. if (empty($path) || $path === '/' || $path === '//' || $path === '/index.php') {
  120. $path = '/';
  121. }
  122. $endsWithIndex = '/webroot/index.php';
  123. $endsWithLength = strlen($endsWithIndex);
  124. if (strlen($path) >= $endsWithLength &&
  125. substr($path, -$endsWithLength) === $endsWithIndex
  126. ) {
  127. $path = '/';
  128. }
  129. return $uri->withPath($path);
  130. }
  131. /**
  132. * Calculate the base directory and webroot directory.
  133. *
  134. * @param \Psr\Http\Message\UriInterface $uri The Uri instance.
  135. * @param array $server The SERVER data to use.
  136. * @return array An array containing the [baseDir, webroot]
  137. */
  138. protected static function getBase($uri, $server)
  139. {
  140. $config = (array)Configure::read('App') + [
  141. 'base' => null,
  142. 'webroot' => null,
  143. 'baseUrl' => null
  144. ];
  145. $base = $config['base'];
  146. $baseUrl = $config['baseUrl'];
  147. $webroot = $config['webroot'];
  148. if ($base !== false && $base !== null) {
  149. return [$base, $base . '/'];
  150. }
  151. if (!$baseUrl) {
  152. $base = dirname(Hash::get($server, 'PHP_SELF'));
  153. // Clean up additional / which cause following code to fail..
  154. $base = preg_replace('#/+#', '/', $base);
  155. $indexPos = strpos($base, '/' . $webroot . '/index.php');
  156. if ($indexPos !== false) {
  157. $base = substr($base, 0, $indexPos) . '/' . $webroot;
  158. }
  159. if ($webroot === basename($base)) {
  160. $base = dirname($base);
  161. }
  162. if ($base === DIRECTORY_SEPARATOR || $base === '.') {
  163. $base = '';
  164. }
  165. $base = implode('/', array_map('rawurlencode', explode('/', $base)));
  166. return [$base, $base . '/'];
  167. }
  168. $file = '/' . basename($baseUrl);
  169. $base = dirname($baseUrl);
  170. if ($base === DIRECTORY_SEPARATOR || $base === '.') {
  171. $base = '';
  172. }
  173. $webrootDir = $base . '/';
  174. $docRoot = Hash::get($server, 'DOCUMENT_ROOT');
  175. $docRootContainsWebroot = strpos($docRoot, $webroot);
  176. if (!empty($base) || !$docRootContainsWebroot) {
  177. if (strpos($webrootDir, '/' . $webroot . '/') === false) {
  178. $webrootDir .= $webroot . '/';
  179. }
  180. }
  181. return [$base . $file, $webrootDir];
  182. }
  183. }