TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/*
|
||||
* This file is part of the TYPO3 CMS project.
|
||||
*
|
||||
* It is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, either version 2
|
||||
* of the License, or any later version.
|
||||
*
|
||||
* For the full copyright and license information, please read the
|
||||
* LICENSE.txt file that was distributed with this source code.
|
||||
*
|
||||
* The TYPO3 project - inspiring people to share!
|
||||
*/
|
||||
|
||||
namespace TYPO3\CMS\Core\Http;
|
||||
|
||||
use Psr\Http\Message\ServerRequestFactoryInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Message\UploadedFileInterface;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\AsAlias;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
|
||||
/**
|
||||
* Class to create ServerRequest objects
|
||||
*
|
||||
* Highly inspired by https://github.com/phly/http/
|
||||
*
|
||||
* @internal Note that this is not public API yet.
|
||||
*/
|
||||
#[AsAlias(ServerRequestFactoryInterface::class, public: true)]
|
||||
readonly class ServerRequestFactory implements ServerRequestFactoryInterface
|
||||
{
|
||||
/**
|
||||
* Create a new server request.
|
||||
*
|
||||
* Note that server-params are taken precisely as given - no parsing/processing
|
||||
* of the given values is performed, and, in particular, no attempt is made to
|
||||
* determine the HTTP method or URI, which must be provided explicitly.
|
||||
*
|
||||
* @param string $method The HTTP method associated with the request.
|
||||
* @param UriInterface|string $uri The URI associated with the request.
|
||||
* @param array $serverParams Array of SAPI parameters with which to seed the generated request instance.
|
||||
*/
|
||||
public function createServerRequest(string $method, $uri, array $serverParams = []): ServerRequestInterface
|
||||
{
|
||||
return new ServerRequest($uri, $method, null, [], $serverParams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a request from the original superglobal variables.
|
||||
*
|
||||
* @return ServerRequest
|
||||
* @throws \InvalidArgumentException when invalid file values given
|
||||
* @internal Note that this is not public API yet.
|
||||
*/
|
||||
public static function fromGlobals()
|
||||
{
|
||||
$serverParameters = $_SERVER;
|
||||
$headers = static::prepareHeaders($serverParameters);
|
||||
|
||||
$method = $serverParameters['REQUEST_METHOD'] ?? 'GET';
|
||||
try {
|
||||
// Note an early middleware creates NormalizedParams again to attach it as request attribute.
|
||||
// Doing this twice per request is considered ok for now since NormalizedParams is constructed
|
||||
// quite quickly with just a few string operations in it. We also may not want ot attach NormalizedParams
|
||||
// as request attribute here already to not 'pollute' ServerRequest early. Another option is to
|
||||
// hand over NormalizedParams to this method and have callers handle the decision if the attribute
|
||||
// is attached as request attribute on their own.
|
||||
$uri = new Uri(NormalizedParams::createFromServerParams($serverParameters)->getRequestUrl());
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
if (Environment::isCli()) {
|
||||
throw new InvalidRequestUrlOnCliException(
|
||||
'Usage of ' . __METHOD__ . ' on CLI is discouraged. In case you rely on the method, you have to fake a valid request URL using $_SERVER.',
|
||||
1701105725,
|
||||
$e,
|
||||
);
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$request = new ServerRequest(
|
||||
$uri,
|
||||
$method,
|
||||
'php://input',
|
||||
$headers,
|
||||
$serverParameters,
|
||||
static::normalizeUploadedFiles($_FILES)
|
||||
);
|
||||
if (!empty($_COOKIE)) {
|
||||
$request = $request->withCookieParams($_COOKIE);
|
||||
}
|
||||
if (!empty($_GET)) {
|
||||
$request = $request->withQueryParams($_GET);
|
||||
}
|
||||
$parsedBody = $_POST;
|
||||
if (empty($parsedBody) && in_array($method, ['PUT', 'PATCH', 'DELETE'])) {
|
||||
parse_str((string)file_get_contents('php://input'), $parsedBody);
|
||||
}
|
||||
if (!empty($parsedBody)) {
|
||||
$request = $request->withParsedBody($parsedBody);
|
||||
}
|
||||
return $request;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch headers from $_SERVER variables
|
||||
* which are only the ones starting with HTTP_* and CONTENT_*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function prepareHeaders(array $server)
|
||||
{
|
||||
$headers = [];
|
||||
foreach ($server as $key => $value) {
|
||||
if (!is_string($key)) {
|
||||
continue;
|
||||
}
|
||||
if (str_starts_with($key, 'HTTP_COOKIE')) {
|
||||
// Cookies are handled using the $_COOKIE superglobal
|
||||
continue;
|
||||
}
|
||||
if ($value !== '') {
|
||||
if (str_starts_with($key, 'HTTP_')) {
|
||||
$name = str_replace('_', ' ', substr($key, 5));
|
||||
$name = str_replace(' ', '-', ucwords(strtolower($name)));
|
||||
$name = strtolower($name);
|
||||
$headers[$name] = $value;
|
||||
} elseif (str_starts_with($key, 'CONTENT_')) {
|
||||
$name = substr($key, 8); // Content-
|
||||
$name = 'Content-' . (($name === 'MD5') ? $name : ucfirst(strtolower($name)));
|
||||
$name = strtolower($name);
|
||||
$headers[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize uploaded files
|
||||
*
|
||||
* Transforms each value into an UploadedFileInterface instance, and ensures that nested arrays are normalized.
|
||||
*
|
||||
* @param array $files
|
||||
* @return array
|
||||
* @throws \InvalidArgumentException for unrecognized values
|
||||
*/
|
||||
protected static function normalizeUploadedFiles(array $files)
|
||||
{
|
||||
$normalizedFileUploads = [];
|
||||
foreach ($files as $key => $value) {
|
||||
if ($value instanceof UploadedFileInterface) {
|
||||
$normalizedFileUploads[$key] = $value;
|
||||
} elseif (is_array($value)) {
|
||||
if (isset($value['tmp_name'])) {
|
||||
$uploadedFiles = self::createUploadedFile($value);
|
||||
if ($uploadedFiles) {
|
||||
$normalizedFileUploads[$key] = $uploadedFiles;
|
||||
}
|
||||
} else {
|
||||
$normalizedFileUploads[$key] = self::normalizeUploadedFiles($value);
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Invalid value in files specification.', 1436717282);
|
||||
}
|
||||
}
|
||||
return $normalizedFileUploads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and return an UploadedFile instance from a $_FILES specification.
|
||||
*
|
||||
* If the specification represents an array of values, this method will
|
||||
* recursively resolve uploaded files.
|
||||
*
|
||||
* @param array $value $_FILES structure
|
||||
* @return UploadedFileInterface[]|UploadedFileInterface|null
|
||||
*/
|
||||
protected static function createUploadedFile(array $value)
|
||||
{
|
||||
if (is_array($value['tmp_name'])) {
|
||||
$files = [];
|
||||
foreach (array_keys($value['tmp_name']) as $key) {
|
||||
$data = [
|
||||
'tmp_name' => $value['tmp_name'][$key],
|
||||
'error' => $value['error'][$key],
|
||||
'name' => $value['name'][$key],
|
||||
'type' => $value['type'][$key],
|
||||
];
|
||||
if (isset($value['size'][$key])) {
|
||||
$data['size'] = $value['size'][$key];
|
||||
}
|
||||
$result = self::createUploadedFile($data);
|
||||
if ($result) {
|
||||
$files[$key] = $result;
|
||||
}
|
||||
}
|
||||
return $files;
|
||||
}
|
||||
if (!empty($value['tmp_name'])) {
|
||||
return new UploadedFile($value['tmp_name'], $value['size'] ?? 0, $value['error'], $value['name'], $value['type']);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user