Отправка заголовков
<?php
declare(strict_types=1);
// header() MUST be called before any output!
header('Content-Type: application/json');
header('X-Custom-Header: value');
// Redirect
header('Location: /dashboard');
exit; // ALWAYS exit after redirect!
// HTTP status code
http_response_code(404);
header('HTTP/1.1 404 Not Found');
// Content-Type examples
header('Content-Type: text/html; charset=UTF-8');
header('Content-Type: application/json');
header('Content-Type: application/pdf');
header('Content-Type: text/csv');
// Force download
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="report.csv"');
header('Content-Length: ' . filesize('/tmp/report.csv'));
readfile('/tmp/report.csv');
exit;
// Cache control
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Pragma: no-cache');
header('Expires: 0');
// CORS headers
header('Access-Control-Allow-Origin: https://example.com');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE');
header('Access-Control-Allow-Headers: Content-Type, Authorization');
Ловушка экзамена:
header()должен вызываться ДО любого вывода (включая пробелы и BOM перед<?php). Вызов после вывода генерирует Warning: "Cannot modify header information - headers already sent". Исключение: если включён output buffering.
http_response_code
<?php
declare(strict_types=1);
// Set HTTP status code
http_response_code(200); // OK
http_response_code(201); // Created
http_response_code(204); // No Content
http_response_code(301); // Moved Permanently
http_response_code(302); // Found (temporary redirect)
http_response_code(400); // Bad Request
http_response_code(401); // Unauthorized
http_response_code(403); // Forbidden
http_response_code(404); // Not Found
http_response_code(405); // Method Not Allowed
http_response_code(500); // Internal Server Error
// Get current status code
$currentCode = http_response_code();
// Common patterns
function jsonResponse(mixed $data, int $code = 200): never
{
http_response_code($code);
header('Content-Type: application/json');
echo json_encode($data, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
exit;
}
// Usage
jsonResponse(['error' => 'Not found'], 404);
Cookies
<?php
declare(strict_types=1);
// Set cookie
setcookie('theme', 'dark', [
'expires' => time() + 86400 * 30, // 30 days
'path' => '/',
'domain' => '.example.com',
'secure' => true, // HTTPS only
'httponly' => true, // No JS access
'samesite' => 'Lax', // CSRF protection
]);
// Read cookie
$theme = $_COOKIE['theme'] ?? 'light';
// Delete cookie (set expiration in the past)
setcookie('theme', '', [
'expires' => time() - 3600,
'path' => '/',
]);
// Old syntax (still works but less readable)
setcookie('name', 'value', time() + 3600, '/', '.example.com', true, true);
// name value expires path domain secure httponly
Запомни: Cookies отправляются в HTTP-заголовках, поэтому
setcookie()тоже должен вызываться ДО вывода. Cookie сhttponly=trueнедоступен из JavaScript (document.cookie).secure=trueотправляет cookie только по HTTPS.
Cookie vs Session
| Аспект | Cookie | Session |
|---|---|---|
| Хранение | На клиенте | На сервере |
| Размер | ~4KB | Неограничен |
| Безопасность | Виден пользователю | Скрыт |
| Время жизни | До expiration | До gc_maxlifetime |
| Количество | ~50 на домен | 1 (через ID в cookie) |
Output Buffering
<?php
declare(strict_types=1);
// Start output buffering
ob_start();
// Now you can send headers AFTER output
echo '<html>';
echo '<body>Hello</body>';
// Still can set headers!
header('X-Custom: value');
http_response_code(200);
// Get buffer contents
$content = ob_get_contents();
// Send and end buffering
ob_end_flush();
// Or discard buffer
// ob_end_clean();
// Nested buffers
ob_start();
echo 'Level 1';
ob_start();
echo 'Level 2';
$inner = ob_get_clean(); // Get and clean inner buffer
echo 'Back to Level 1';
$outer = ob_get_clean(); // Get and clean outer buffer
// Useful functions
echo ob_get_level(); // Current nesting level
echo ob_get_length(); // Buffer size
ob_implicit_flush(true); // Auto-flush after each output
Запомни: Output buffering позволяет отправлять заголовки после вывода, потому что вывод накапливается в буфере.
ob_start()начинает буферизацию,ob_end_flush()отправляет буфер,ob_end_clean()— очищает без отправки.
HTTP Authentication
<?php
declare(strict_types=1);
// Basic Authentication
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My App"');
http_response_code(401);
exit('Authentication required');
}
$username = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
if ($username !== 'admin' || $password !== 'secret') {
http_response_code(403);
exit('Access denied');
}
echo "Welcome, {$username}!";
headers_sent и headers_list
<?php
declare(strict_types=1);
// Check if headers were already sent
if (!headers_sent($file, $line)) {
header('Content-Type: application/json');
} else {
// Headers already sent in $file at $line
error_log("Headers sent in {$file} on line {$line}");
}
// List all headers that will be sent
$headers = headers_list();
// ['Content-Type: text/html; charset=UTF-8', 'X-Powered-By: PHP/8.4']
// Remove a header
header_remove('X-Powered-By');
// Remove all headers
header_remove();