ServerRequestFactory.php 6.3 KB

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