MidТеория3 min

HTTP и заголовки

header(), http_response_code, cookies, authentication

Отправка заголовков

<?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 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();

Вопросы с экзамена ZCE

Проверь себя

5 из 13

Какой HTTP-код статуса запрашивает у пользователя предоставление учётных данных?

Что обязательно после `header('Location: /page')`?

Какой класс HTTP-кодов статуса используется для обозначения ошибок?

Какие из следующих суперглобальных массивов можно использовать для получения cookie со стороны клиента? Каждый правильный ответ является полным решением. Выберите все подходящие.

Выберите все правильные варианты

Как удалить cookie?