symfony/http-foundation
Symfony HttpFoundation provides an object-oriented API for HTTP: requests, responses, headers, cookies, sessions, and file uploads. It normalizes PHP’s globals into consistent objects, making it easier to build and test web applications and middleware.
## Getting Started
### Minimal Steps
1. **Installation**:
```bash
composer require symfony/http-foundation:^8.1
Note: Laravel 10+ includes Symfony HTTP Foundation v8.1+ by default (via illuminate/http). For older Laravel versions, update manually or rely on Laravel’s built-in Request/Response wrappers.
First Use Case (Laravel 10+): Access the request object in a controller (now leveraging v8.1 features):
use Symfony\Component\HttpFoundation\Request;
public function handle(Request $request)
{
$method = $request->getMethod(); // 'GET', 'POST', etc.
$query = $request->query->all(); // Query params
$headers = $request->headers->get('X-Custom-Header', ''); // Case-insensitive access
}
Key Classes to Explore (v8.1+):
Request: Enhanced header/cookie handling (see #64269 fixes).Response: Improved send() behavior (now throws exceptions for invalid content).UploadedFile: Stricter validation (e.g., getErrorMessage() now returns false for invalid files).HeaderBag: Case-insensitive header access with stricter parsing.Where to Look First:
Illuminate\Http\Request (extends Symfony’s Request).// Case-insensitive, returns first value (throws if missing)
$token = $request->headers->get('Authorization');
// Safe fallback:
$token = $request->headers->get('Authorization', '');
$cookie = $request->cookies->get('user_token');
if ($cookie === null) {
// Cookie not set (not expired or invalid)
}
if ($request->isMethod('POST') || $request->isMethod('PUT')) {
// ...
}
$response = new Response(json_encode(['data' => 'value']), 200, [
'Content-Type' => 'application/json',
]);
// Throws \InvalidArgumentException if content is not string/array
$file = $request->files->get('file');
if ($file && $file->isValid()) {
$response = new BinaryFileResponse($file->getPathname());
$response->setContentDisposition(
HeaderUtils::makeDisposition(
HeaderUtils::DISPOSITION_ATTACHMENT,
$file->getClientOriginalName()
)
);
return $response;
}
$redirect = new RedirectResponse('/dashboard');
$redirect->headers->set('Cache-Control', 'no-store');
return $redirect;
$file = $request->files->get('avatar');
if (!$file->isValid()) {
throw new \RuntimeException($file->getErrorMessage() ?: 'Invalid upload');
}
// v8.1: getErrorMessage() returns false for no error (not null)
$file->moveTo($storagePath . '/' . $file->getClientOriginalName());
// v8.1: UploadedFile now implements Stringable (can use in templates)
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
public function handle(Request $request, Closure $next): Response
{
if (!$request->headers->has('X-API-Key')) {
throw new AccessDeniedHttpException('API key required');
}
return $next($request);
}
protected $middleware = [
\App\Http\Middleware\ApiKeyMiddleware::class,
];
$request = Request::create(
'/test',
'GET',
[],
[],
[],
['HTTP_X_API_KEY' => 'secret']
);
$response = $this->app->handle($request);
$this->assertEquals(200, $response->getStatusCode());
$file = UploadedFile::create(
__DIR__.'/test.pdf',
'test.pdf',
'application/pdf',
null,
true // Test mode (no actual upload)
);
$request = Request::create('/upload', 'POST', [], [], [], ['file' => $file]);
Leverage Symfony v8.1 in Laravel:
Request directly in service containers:
public function __construct(private Request $request) {}
UploadedFile for validation (Laravel’s Illuminate\Http\UploadedFile extends Symfony’s version):
$file = $request->file('avatar');
$file->validate(); // Uses Symfony's validation under the hood
New v8.1 Features in Laravel:
use Symfony\Component\HttpFoundation\HeaderUtils;
$disposition = HeaderUtils::makeDisposition(
HeaderUtils::DISPOSITION_INLINE,
'report.pdf'
);
$response = Response::json(['success' => true]);
// Internally uses Symfony's Response with proper headers
Standalone Usage (Non-Laravel):
$_SERVER['REQUEST_METHOD'] = 'POST';
$request = Request::createFromGlobals();
$response = new Response('OK');
$response->send(); // Throws on invalid content (v8.1 strictness)
UploadedFile::getErrorMessage():
Returns false for no error (previously null). Update checks:
if ($file->getError() !== UploadedFile::UPLOAD_ERR_OK) {
// Handle error
}
Response::send():
Now throws \LogicException if content is invalid (e.g., non-string JSON).get() with fallback:
$header = $request->headers->get('Content-Type', 'text/plain');
Request Creation in Tests:
Request::create() does not populate $_SERVER/$_GET by default. Use Request::createFromGlobals() for real data or mock all globals:
$_SERVER['REQUEST_METHOD'] = 'GET';
$_GET['param'] = 'value';
$request = Request::create('/test');
RequestTestCase in Symfony’s test suite for helpers.Session Handling:
$session = $request->getSession();
$session->start(); // Required in v8.1 for new sessions
$session->set('key', 'value');
File Upload Validation:
UploadedFile::isValid() now checks:
$file->moveTo($storagePath); // Critical in v8.1
Header Parsing Quirks:
How can I help you explore Laravel packages today?