Куки в PHP
http://belarusweb.net
Основы создания сайтов

Куки в PHP

Что такое куки?

Довольно часто в ходе взаимодействия пользователя с сайтом возникает необходимость сохранения небольших объемов данных на стороне пользователя. Примерами могут служить данные, использующиеся для ведения статистики о пользователях, данные о персональных предпочтениях пользователя или же данные для идентификации возвращающихся на сайт пользователей для их последующей авторизации без повторного ввода логина и пароля. Все эти данные передаются на компьютер пользователя в виде куки, где они обрабатываются и сохраняются браузером для дальнейшего использования.

Куки (от англ. cookie) – небольшие объемы именованных строковых данных, которые используются браузером пользователя для хранения информации, необходимой серверу при повторном посещении пользователем страниц сайта.

При первом посещении страницы сайта пользователем веб-сервер возвращает запрашиваемую страницу и в случае необходимости дополнительно передает куки, которые передаются в составе HTTP-ответа и представляют собой текстовые строки в виде 'Set-Cookie: name=value'. Если в браузере пользователя разрешен прием куки, он их запоминает и далее отправляет с каждым последующим HTTP-запросом к сайту. В результате таких манипуляций сервер будет понимать, что текущий запрос связан с предыдущим и сможет предпринимать необходимые действия, например, авторизовывать пользователя или увеличивать счетчик посещений страницы. Упрощенная схема механизма передачи куки показана на рис. 8.7.

Рис 8.7. Упрощенная схема использования куки

Если текущий запрос уже не первый, сервер может изменять текущее значение установленных куки путём отправки новых строк 'Set-Cookie: name=newvalue'. Соответственно браузер, после получения нового HTTP-ответа, заменяет старое куки с тем же name на куки с новым значением newvalue.

Установка куки

Для передачи клиенту куки в PHP предусмотрена сетевая функция setcookie(). Описание данной функции можно посмотреть в официальном справочнике в разделе Справочник функций -> Другие службы -> Network -> Сетевые функции. Использование данной функции показано в примере 8.8.

В качестве аргументов функция принимает:

  • name - строка с именем куки.
  • value - строка, содержащая значение куки, т.е. те самые данные, которые будут использоваться в дальнейшем.
  • expire - целое число, которое устанавливает срок действия куки и представляет собой метку времени Unix, т.е. число секунд, прошедших с начала 1 января 1970 года. Данный аргумент желательно задавать с помощью функции time(), добавляя время в секундах, через которое срок действия куки должен истечь (см. пример 8.8). Если задать 0 или вообще пропустить этот аргумент, срок действия куки истечет с окончанием текущей сессии пользователя (достаточно закрыть окно браузера).
  • path - строка, которая содержит путь к директории на сервере, из которой будет доступно куки. Если задать '/', то куки будет доступно во всем домене, который указан в качестве значения следующего за ним аргумента domain. Если задать '/test/', то куки будет доступно только из директории /test/ и всех ее поддиректорий указанного домена domain. По умолчанию значением является текущая директория, из которой куки устанавливается.
  • domain - строка с именем домена, которому будет доступно данное куки.
  • secure - аргумент принимает логическое значение TRUE или FALSE. Если указать TRUE, то куки будет передаваться только по защищенному HTTPS-соединению. По умолчанию используется значение FALSE, поэтому если аргумент опустить, куки будет передаваться также и по обычному протоколу HTTP. Здесь следует заметить, что при передаче куки от сервера клиенту следить за тем, чтобы куки передавались по защищенному каналу, должен программист веб-сервера (для этого можно использовать, например, элемент суперглобального массива $_SERVER["HTTPS"], который принимает непустое значение, если запрос был произведен через протокол HTTPS).
  • httponly - если в качестве значения данного аргумента указать TRUE, то куки будет доступно только через HTTP-протокол и не будет доступно скриптовым языкам вроде JavaScript. Данная возможность была введена с целью противодействия XSS-атакам, однако вокруг нее довольно часто возникают споры о ее эффективности и целесообразности использования. По умолчанию, опять же, используется значение FALSE.

Все аргументы, за исключением name, являются необязательными, поэтому их можно опускать. Для этого нужно использовать пустую строку ("") или число 0, если опускаемым аргументом является expire.

<?php
//Задаем значения для наших куки
$value_1 = 'Наше первое куки!';
$value_2 = 'Куки на час!';
$value_3 = 'Куки для страниц каталога test нашего сайта';

//Это куки будет действовать до конца сессии
setcookie("cookie_1", $value_1);
//Устанавливаем куки на 1 час
setcookie("cookie_2", $value_2, time()+3600);
//Устанавливаем куки только для страниц каталога test
setcookie("cookie_3", $value_3, 0, "/test/");
?> 

Пример 8.8. Вызов функции setcookie()

Важно помнить, что куки отправляются клиенту вместе с другими HTTP-заголовками. Поэтому, как и любой другой заголовок, куки должны передаваться до того, как будут выведены какие-либо другие данные скрипта, включая тэг <!DOCTYPE html>, а также пустые строки и пробелы.

В случае успешной отправки куки функция setcookie() вернет true (однако это не означает, что браузер правильно примет и обработает куки). Если же что-то пойдет не так, например, перед вызовом данной функции будут выводиться какие-нибудь символы, то куки передано не будет, а функция вернет false.

В конечном итоге, если куки будет правильно установлено клиенту, оно станет доступно при следующей загрузке страницы через суперглобальные массивы $_COOKIE и $_REQUEST. Для более наглядного представления о процессе установки и последующего использования куки изучите пример 8.9, после чего запустите его в браузере (используйте значок планеты) и обновите открывшуюся страницу пару раз. Обратите внимание, если не проявлять активности более десяти секунд, то куки истечет и при следующем посещении страницы нам будет выведено соответствующее сообщение.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">    
    <title>Счетчик посещений страницы</title>
</head>
<body>
  
<?php
	//Проверяем, было ли уже установлено cookie, если да, то
	if(isset($_COOKIE['times'])){  
		//увеличиваем его значение на единицу.
		$t=$_COOKIE['times']+1;
		//Формируем сообщение
		$message="Количество посещений страницы: {$t}";	
	//Если же посещение первое или куки истекли
	}else{
		//присваиваем переменной начальное значение
		$t=0; 
		//Формируем сообщение
		$message="Вы либо давно к нам не заходили, 
		либо посещаете нас впервые!";
	}

	//При каждом посещении страницы обновляем куки
	//Время жизни устанавливаем всего 10 секунд
	setcookie('times',$t,time()+10); 
	//Вывод делаем только после вызова setcookie()!!!
	echo $message;
?>

</body>
</html>

Пример 8.9. Установка и использование куки

Стоит отметить, что значение куки перед отправкой клиенту подвергается URL-кодированию. Однако при обратном получении значение куки декодируется и помещается в переменную с тем же именем, что и имя куки. Если же по каким-то причинам значения не должны кодироваться, нужно использовать функцию setrawcookie(), которая во всем остальном является аналогом функции setcookie().

Куки и массивы

Если данные, которые мы хотим отправить пользователю при помощи куки, хранятся в массиве, нужно каждому куки присвоить имя соответствующего элемента массива, а в качестве значения куки указать значение данного элемента массива (см. пример 8.10). Если массив данных индексированный, можно просто добавить квадратные скобки к имени куки.

<?php
//Запомнили предпочтения пользователя
//Поместили значения массива в куки на месяц
setcookie("menu['color']", "red", time()+60*60*24*30);  
setcookie("menu['width']", "200px", time()+60*60*24*30); 
setcookie("menu['height']", "300px", time()+60*60*24*30); 

//После перезагрузки страницы выведем все куки
if(isset($_COOKIE['menu'])) {  
   //Проще всего использовать цикл foreach
   echo "Выводим данные при помощи цикла foreach <br>";
	foreach ($_COOKIE['menu'] as $name => $value) {
        echo "{$name} : {$value} <br>";
    }
	
	//Однако можно использовать и цикл for
	echo "<br>Выводим данные при помощи цикла for <br>";
	//Для начала поместим имена ключей массива в отдельный массив
	$key_name=array_keys($_COOKIE['menu']);
	//Теперь выведем ключи массива и соответствующие им значения
	for($i=0; $i<count($_COOKIE['menu']); $i++) {
        //Здесь $key_name[$i] - имя i-го ключа массива $key_name
		echo "{$key_name[$i]}: {$_COOKIE['menu'][$key_name[$i]]} <br>";
    }
}
?>

Пример 8.10. Передача данных массивов в куки

Как видно из примера, если в массиве присутствуют три элемента, то и вызовов функции setcookie() также должно быть три. При этом сами функции выполняются в том порядке, в котором они вызывались в коде скрипта. Далее, для получения этих данных на сервере обратно, нужно будет извлечь их из массива с именем куки (в нашем случае это массив $_COOKIE['menu'], который сам является элементом суперглобального массива). Здесь нужно помнить, что хотя на сервере данные и доступны в виде массива, на машине пользователя они все равно хранятся в виде отдельных записей.

Удаление куки

Если возникает необходимость в удалении куки до истечения срока его действия, необходимо просто перезаписать его, указав какое-либо время в прошлом. Это запустит механизм браузера, который отвечает за удаление истекших куки (см. пример 8.11). При этом нужно помнить, что удаляемые куки должны быть заданы с теми же параметрами, что и при установке (изменять нужно только время).

<?php
//Проверяем, был ли уже установлен куки, если да, то
if(isset($_COOKIE['times'])){  
	//увеличиваем его значение на единицу
	$t=$_COOKIE['times']+1; 
//иначе присваиваем переменной начальное значение
}else{
	//Куки возможно не установились, но 1 посещение уже есть
	$t=1; 
}
	
// Если число обращений к странице превысило 3 
if($t>3){  
	//удаляем куки
	setcookie('times',$t+1,time()-100); 
	//обнуляем значение счетчика
	$t=0; 
	//и сообщаем об этом пользователю
	echo 'Куки удален, т.к. вы посетили страницу более 3-х раз!';
//Если число обращений меньше 3, то
}else{  
//обновляем куки при каждом новом посещении
setcookie('times',$t,time()+60); 
//Выводим сообщение пользователю
echo "Количество посещений страницы: {$t}";
}
?>

Пример 8.11. Удаление куки