Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add RequestFactory and RequestTransformer.
The ServerRequestFactory is responsible for: * Building a request from the SAPI super globals. * Extracting the base and webroot directories for backwards compatibility with the CakeRequest. * Updating the request path to reflect only the application's 'virtual path' The RequestTransformer handles: * Converting a PSR7 request object into the equivalent Cake\Network\Http request. * Ensuring that the required routing parameters are set even if the PSR7 request is missing the 'params' attribute. In order to shim the backwards compatibility, we'll use 3 attributes which keep track of CakePHP specific path information and our routing parameters. I felt this was the cleanest approach as I wasn't comfortable subclassing Diactoros\ServerRequest to add methods for these just yet.
- Loading branch information
Showing
4 changed files
with
725 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* Redistributions of files must retain the above copyright notice. | ||
* | ||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @link http://cakephp.org CakePHP(tm) Project | ||
* @since 3.3.0 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\Http; | ||
|
||
use Cake\Core\Configure; | ||
use Cake\Network\Request as CakeRequest; | ||
use Cake\Utility\Hash; | ||
use Psr\Http\Message\ServerRequestInterface as PsrRequest; | ||
|
||
/** | ||
* Translate and transform from PSR7 requests into CakePHP requests. | ||
* | ||
* This is an important step for maintaining backwards compatibility | ||
* with existing CakePHP applications, which depend on the CakePHP request object. | ||
* | ||
* There is no reverse transform as the 'application' cannot return a mutated | ||
* request object. | ||
* | ||
* @internal | ||
*/ | ||
class RequestTransformer | ||
{ | ||
/** | ||
* Transform a PSR7 request into a CakePHP one. | ||
* | ||
* @param \Psr\Http\Message\ServerRequestInterface $request The PSR7 request. | ||
* @return \Cake\Network\Request The transformed request. | ||
*/ | ||
public static function toCake(PsrRequest $request) | ||
{ | ||
$post = $request->getParsedBody(); | ||
$server = $request->getServerParams(); | ||
|
||
$files = static::getFiles($request); | ||
if (!empty($files)) { | ||
$post = Hash::merge($post, $files); | ||
} | ||
|
||
return new CakeRequest([ | ||
'query' => $request->getQueryParams(), | ||
'post' => $post, | ||
'cookies' => $request->getCookieParams(), | ||
'environment' => $server, | ||
'params' => static::getParams($request), | ||
'url' => $request->getUri()->getPath(), | ||
'base' => $request->getAttribute('base', ''), | ||
'webroot' => $request->getAttribute('webroot', '/'), | ||
]); | ||
} | ||
|
||
/** | ||
* Extract the routing parameters out of the request object. | ||
* | ||
* @param \Psr\Http\Message\ServerRequestInterface $request The request to extract params from. | ||
* @return array The routing parameters. | ||
*/ | ||
protected static function getParams(PsrRequest $request) | ||
{ | ||
$params = (array)$request->getAttribute('params', []); | ||
$params += [ | ||
'plugin' => null, | ||
'controller' => null, | ||
'action' => null, | ||
'_ext' => null, | ||
'pass' => [] | ||
]; | ||
return $params; | ||
} | ||
|
||
/** | ||
* Extract the uploaded files out of the request object. | ||
* | ||
* CakePHP expects to get arrays of file information and | ||
* not the parsed objects that PSR7 requests contain. Downsample the data here. | ||
* | ||
* @param \Psr\Http\Message\ServerRequestInterface $request The request to extract files from. | ||
* @return array The routing parameters. | ||
*/ | ||
protected static function getFiles($request) | ||
{ | ||
return static::convertFiles([], $request->getUploadedFiles()); | ||
} | ||
|
||
/** | ||
* Convert a nested array of files to arrays. | ||
* | ||
* @param array $data The data to add files to. | ||
* @param array $files The file objects to convert. | ||
* @param string $path The current array path. | ||
* @return array Converted file data | ||
*/ | ||
protected static function convertFiles($data, $files, $path = '') | ||
{ | ||
foreach ($files as $key => $file) { | ||
$newPath = $path; | ||
if ($newPath === '') { | ||
$newPath = $key; | ||
} | ||
if ($newPath !== $key) { | ||
$newPath .= '.' . $key; | ||
} | ||
|
||
if (is_array($file)) { | ||
$data = static::convertFiles($data, $file, $newPath); | ||
} else { | ||
$data = Hash::insert($data, $newPath, static::convertFile($file)); | ||
} | ||
} | ||
return $data; | ||
} | ||
|
||
/** | ||
* Convert a single file back into an array. | ||
* | ||
* @param \Psr\Http\Message\UploadedFileInterface $file The file to convert. | ||
* @return array | ||
*/ | ||
protected static function convertFile($file) | ||
{ | ||
return [ | ||
'name' => $file->getClientFilename(), | ||
'type' => $file->getClientMediaType(), | ||
'tmp_name' => $file->getStream()->getMetadata('uri'), | ||
'error' => $file->getError(), | ||
'size' => $file->getSize(), | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
<?php | ||
/** | ||
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org) | ||
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* | ||
* Licensed under The MIT License | ||
* For full copyright and license information, please see the LICENSE.txt | ||
* Redistributions of files must retain the above copyright notice. | ||
* | ||
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org) | ||
* @link http://cakephp.org CakePHP(tm) Project | ||
* @since 3.3.0 | ||
* @license http://www.opensource.org/licenses/mit-license.php MIT License | ||
*/ | ||
namespace Cake\Http; | ||
|
||
use Cake\Core\Configure; | ||
use Cake\Utility\Hash; | ||
use Zend\Diactoros\ServerRequestFactory as BaseFactory; | ||
|
||
/** | ||
* Factory for making ServerRequest instances. | ||
* | ||
* This subclass adds in CakePHP specific behavior to populate | ||
* the basePath and webroot attributes. Furthermore the Uri's path | ||
* is corrected to only contain the 'virtual' path for the request. | ||
*/ | ||
abstract class ServerRequestFactory extends BaseFactory | ||
{ | ||
/** | ||
* {@inheritDoc} | ||
*/ | ||
public static function fromGlobals( | ||
array $server = null, | ||
array $query = null, | ||
array $body = null, | ||
array $cookies = null, | ||
array $files = null | ||
) { | ||
$request = parent::fromGlobals($server, $query, $body, $cookies, $files); | ||
list($base, $webroot) = static::getBase($request); | ||
$request = $request->withAttribute('base', $base) | ||
->withAttribute('webroot', $webroot); | ||
if ($base) { | ||
$request = static::updatePath($base, $request); | ||
} | ||
return $request; | ||
} | ||
|
||
/** | ||
* Updates the request URI to remove the base directory. | ||
* | ||
* @param string $base The base path to remove. | ||
* @param \Psr\Http\Message\ServerRequestInterface $request The request to modify. | ||
* @return \Psr\Http\Message\ServerRequestInterface The modified request. | ||
*/ | ||
protected static function updatePath($base, $request) | ||
{ | ||
$uri = $request->getUri(); | ||
$path = $uri->getPath(); | ||
if (strlen($base) > 0 && strpos($path, $base) === 0) { | ||
$path = substr($path, strlen($base)); | ||
} | ||
if (empty($path) || $path === '/' || $path === '//' || $path === '/index.php') { | ||
$path = '/'; | ||
} | ||
$endsWithIndex = '/webroot/index.php'; | ||
$endsWithLength = strlen($endsWithIndex); | ||
if (strlen($path) >= $endsWithLength && | ||
substr($path, -$endsWithLength) === $endsWithIndex | ||
) { | ||
$path = '/'; | ||
} | ||
return $request->withUri($uri->withPath($path)); | ||
} | ||
|
||
/** | ||
* Calculate the base directory and webroot directory. | ||
* | ||
* This code is a copy/paste from Cake\Network\Request::_base() | ||
* | ||
* @param \Psr\Http\Message\ServerRequestInterface | ||
* @return array An array containing the [baseDir, webroot] | ||
*/ | ||
protected static function getBase($request) | ||
{ | ||
$path = $request->getUri()->getPath(); | ||
$server = $request->getServerParams(); | ||
|
||
$base = $webroot = $baseUrl = null; | ||
$config = Configure::read('App'); | ||
extract($config); | ||
|
||
if ($base !== false && $base !== null) { | ||
return [$base, $base . '/']; | ||
} | ||
|
||
if (!$baseUrl) { | ||
$base = dirname(Hash::get($server, 'PHP_SELF')); | ||
// Clean up additional / which cause following code to fail.. | ||
$base = preg_replace('#/+#', '/', $base); | ||
|
||
$indexPos = strpos($base, '/' . $webroot . '/index.php'); | ||
if ($indexPos !== false) { | ||
$base = substr($base, 0, $indexPos) . '/' . $webroot; | ||
} | ||
if ($webroot === basename($base)) { | ||
$base = dirname($base); | ||
} | ||
|
||
if ($base === DIRECTORY_SEPARATOR || $base === '.') { | ||
$base = ''; | ||
} | ||
$base = implode('/', array_map('rawurlencode', explode('/', $base))); | ||
return [$base, $base . '/']; | ||
} | ||
|
||
$file = '/' . basename($baseUrl); | ||
$base = dirname($baseUrl); | ||
|
||
if ($base === DIRECTORY_SEPARATOR || $base === '.') { | ||
$base = ''; | ||
} | ||
$webrootDir = $base . '/'; | ||
|
||
$docRoot = Hash::get($server, 'DOCUMENT_ROOT'); | ||
$docRootContainsWebroot = strpos($docRoot, $webroot); | ||
|
||
if (!empty($base) || !$docRootContainsWebroot) { | ||
if (strpos($webrootDir, '/' . $webroot . '/') === false) { | ||
$webrootDir .= $webroot . '/'; | ||
} | ||
} | ||
return [$base . $file, $webrootDir]; | ||
} | ||
} |
Oops, something went wrong.