Php-сессия случайно потеряна и не может понять, почему
Я заплатил программисту, чтобы сделать корзину магазина script для работы с API Spreadshirt. Все работает отлично, за исключением того, что корзина продолжает опустошать себя. Я думаю, что сессия потеряна в какой-то момент, поэтому script создает еще один BasketId
.
Я попытался найти, была ли какая-то конкретная причина, которая произошла, без каких-либо успехов... Я не могу воспроизвести ошибку. Это случается случайным образом без какой-либо причины. Закрытие браузера, сброс apache или даже всего веб-сервера не вызовет потеря сессии.
У меня есть два разных сценария, работающих с куки файлами в одном домене, и у них нет проблем (один из них - это cookie для сеанса входа в admin, а другой файл cookie - для сохранения последних просмотренных статей в магазине )
Я пробовал все решения, найденные в google, без каких-либо успехов: редактирование php.ini
, форсирование настроек ini через php, метод htaccess
,...
Здесь часть сеансов моей phpinfo: http://gyazo.com/168e2144ddd9ee368a05754dfd463021
shop-ajax.php
(обработка сеанса @строка 18)
ini_set('session.cookie_domain', '.mywebsite.com' );
header("Pragma: no-cache");
header("Cache-Control: no-store, no-cache, max-age=0, must-revalidate");
$language = addslashes($_GET['l']);
$shopid = addslashes($_GET['shop']);
// if($_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {
// die("no direct access allowed");
// }
if(!session_id()) {
$lifetime=60 * 60 * 24 * 365;
$domain = ".mywebsite.com";
session_set_cookie_params($lifetime,"/",$domain);
@session_start();
}
// Configuration
$config['ShopSource'] = "com";
$config['ShopId'] = $shopid;
$config['ShopKey'] = "*****";
$config['ShopSecret'] = "*****";
/*
* add an article to the basket
*/
if (isset($_POST['size']) && isset($_POST['appearance']) && isset($_POST['quantity'])) {
/*
* create an new basket if not exist
*/
if (!isset($_SESSION['basketUrl'])) {
/*
* get shop xml
*/
$stringApiUrl = 'http://api.spreadshirt.'.$config['ShopSource'].'/api/v1/shops/' . $config['ShopId'];
$stringXmlShop = oldHttpRequest($stringApiUrl, null, 'GET');
if ($stringXmlShop[0]!='<') die($stringXmlShop);
$objShop = new SimpleXmlElement($stringXmlShop);
if (!is_object($objShop)) die('Basket not loaded');
/*
* create the basket
*/
$namespaces = $objShop->getNamespaces(true);
$basketUrl = createBasket('net', $objShop, $namespaces);
$_SESSION['basketUrl'] = $basketUrl;
$_SESSION['namespaces'] = $namespaces;
/*
* get the checkout url
*/
$checkoutUrl = checkout($_SESSION['basketUrl'], $_SESSION['namespaces']);
// basket language workaround
if ($language=="fr") {
if (!strstr($checkoutUrl,'/fr')) {
$checkoutUrl = str_replace("spreadshirt.com","spreadshirt.com/fr",$checkoutUrl);
}
}
$_SESSION['checkoutUrl'] = $checkoutUrl;
}
/*
Workaround for not having the appearance id :(
*/
if ($_POST['appearance']==0) {
$stringApiArticleUrl = 'http://api.spreadshirt.'.$config['ShopSource'].'/api/v1/shops/' . $config['ShopId'].'/articles/'.intval($_POST['article']).'?fullData=true';
$stringXmlArticle = oldHttpRequest($stringApiArticleUrl, null, 'GET');
if ($stringXmlArticle[0]!='<') die($stringXmlArticle);
$objArticleShop = new SimpleXmlElement($stringXmlArticle);
if (!is_object($objArticleShop)) die('Article not loaded');
$_POST['appearance'] = intval($objArticleShop->product->appearance['id']);
}
/*
* article data to be sent to the basket resource
*/
$data = array(
'articleId' => intval($_POST['article']),
'size' => intval($_POST['size']),
'appearance' => intval($_POST['appearance']),
'quantity' => intval($_POST['quantity']),
'shopId' => $config['ShopId']
);
/*
* add to basket
*/
addBasketItem($_SESSION['basketUrl'] , $_SESSION['namespaces'] , $data);
$basketData = prepareBasket();
echo json_encode(array("c" => array("u" => $_SESSION['checkoutUrl'],"q" => $basketData[0],"l" => $basketData[1])));
}
// no call, just read basket if not empty
if (isset($_GET['basket'])) {
if (array_key_exists('basketUrl',$_SESSION) && !empty($_SESSION['basketUrl'])) {
$basketData = prepareBasket();
echo json_encode(array("c" => array("u" => $_SESSION['checkoutUrl'],"q" => $basketData[0],"l" => $basketData[1])));
} else {
echo json_encode(array("c" => array("u" => "","q" => 0,"l" => "")));
}
}
function prepareBasket() {
$intInBasket=0;
if (isset($_SESSION['basketUrl'])) {
$basketItems=getBasket($_SESSION['basketUrl']);
if(!empty($basketItems)) {
foreach($basketItems->basketItems->basketItem as $item) {
$intInBasket += $item->quantity;
}
}
}
$l = "";
$pQ = parse_url($_SESSION['checkoutUrl']);
if (preg_match("#^basketId\=([0-9a-f\-])*$#i", $pQ['query'])) {
$l = $pQ['query'];
}
return array($intInBasket,$l);
}
// Additional functions
function addBasketItem($basketUrl, $namespaces, $data) {
global $config;
$basketItemsUrl = $basketUrl . "/items";
$basketItem = new SimpleXmlElement('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<basketItem xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://api.spreadshirt.net">
<quantity>' . $data['quantity'] . '</quantity>
<element id="' . $data['articleId'] . '" type="sprd:article" xlink:href="#" onclick="location.href='http://api.spreadshirt.'.$config['ShopSource'].'/api/v1/shops/' . $data['shopId'] . '/articles/' . $data['articleId'] . ''; return false;">
<properties>
<property key="appearance">' . $data['appearance'] . '</property>
<property key="size">' . $data['size'] . '</property>
</properties>
</element>
<links>
<link type="edit" xlink:href="#" onclick="location.href='http://' . $data['shopId'] .'.spreadshirt.' .$config['ShopSource'].'/-A' . $data['articleId'] . ''; return false;"/>
<link type="continueShopping" xlink:href="#" onclick="location.href='http://' . $data['shopId'].'.spreadshirt.'.$config['ShopSource'].''; return false;"/>
</links>
</basketItem>');
$header = array();
$header[] = createAuthHeader("POST", $basketItemsUrl);
$header[] = "Content-Type: application/xml";
$result = oldHttpRequest($basketItemsUrl, $header, 'POST', $basketItem->asXML());
}
function createBasket($platform, $shop, $namespaces) {
$basket = new SimpleXmlElement('<basket xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://api.spreadshirt.net">
<shop id="' . $shop['id'] . '"/>
</basket>');
$attributes = $shop->baskets->attributes($namespaces['xlink']);
$basketsUrl = $attributes->href;
$header = array();
$header[] = createAuthHeader("POST", $basketsUrl);
$header[] = "Content-Type: application/xml";
$result = oldHttpRequest($basketsUrl, $header, 'POST', $basket->asXML());
$basketUrl = parseHttpHeaders($result, "Location");
return $basketUrl;
}
function checkout($basketUrl, $namespaces) {
$basketCheckoutUrl = $basketUrl . "/checkout";
$header = array();
$header[] = createAuthHeader("GET", $basketCheckoutUrl);
$header[] = "Content-Type: application/xml";
$result = oldHttpRequest($basketCheckoutUrl, $header, 'GET');
$checkoutRef = new SimpleXMLElement($result);
$refAttributes = $checkoutRef->attributes($namespaces['xlink']);
$checkoutUrl = (string)$refAttributes->href;
return $checkoutUrl;
}
/*
* functions to build headers
*/
function createAuthHeader($method, $url) {
global $config;
$time = time() *1000;
$data = "$method $url $time";
$sig = sha1("$data ".$config['ShopSecret']);
return "Authorization: SprdAuth apiKey=\"".$config['ShopKey']."\", data=\"$data\", sig=\"$sig\"";
}
function parseHttpHeaders($header, $headername) {
$retVal = array();
$fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
foreach($fields as $field) {
if (preg_match('/(' . $headername . '): (.+)/m', $field, $match)) {
return $match[2];
}
}
return $retVal;
}
function getBasket($basketUrl) {
$header = array();
$basket = "";
if (!empty($basketUrl)) {
$header[] = createAuthHeader("GET", $basketUrl);
$header[] = "Content-Type: application/xml";
$result = oldHttpRequest($basketUrl, $header, 'GET');
$basket = new SimpleXMLElement($result);
}
return $basket;
}
function oldHttpRequest($url, $header = null, $method = 'GET', $data = null, $len = null) {
switch ($method) {
case 'GET':
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
if (!is_null($header)) curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
break;
case 'POST':
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_POST, true); //not createBasket but addBasketItem
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
break;
}
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
?>
Также есть две другие части script: форма для добавления образца tshirt в корзину (example.php)
и script для вызова ajax (shop-controller.js)
. Можете опубликовать его, если это необходимо, но там нет обработки сеанса.
update - Возможно, проблема не связана с сеансами. Идентификатор BasketId потерян, но PHPSESSID остается тем же самым в кукисах браузера.
Я провел следующие тесты за последние 3 дня (тестировался с разными компьютерами и браузерами):
-
Очистите cookie браузера, а затем запустите новый сеанс во второй половине дня.
-
Добавьте 1 элемент в корзину, я записываю BasketId и проверяю файлы cookie браузеров для записи PHPSESSID
-
Обычно всегда около полуночи, сама корзина пуста
-
PHPSESSID остается прежним в моих браузере, даже после того, как корзина пуста сама.
-
Однако BASKETID не то же самое, тот, который используется во второй половине дня, теряется, а новый регенерируется.
Сервер - CentOS 5.9 - Версия PHP 5.2.9 (из OVH). Выделенный сервер на выделенном IP-адресе.
Ответы
Ответ 1
Сначала вам нужно выяснить, есть ли проблема в сборе мусора сеанса или логической ошибке в коде. Для этого вы можете:
// Add this right after session_start()
if (!isset($_SESSION['mySessionCheck'])) {
$_SESSION['mySessionCheck'] = "This session (" . session_id() . ") started " . date("Y-m-d H:i:s");
}
// For HTML pages, add this:
echo '<!-- ' . $_SESSION['mySessionCheck'] . ' -->';
// For AJAX pages, add "mySessionCheck" to the JSON response:
echo json_encode(
array(
"c" => array(
"u" => $_SESSION['checkoutUrl'],
"q" => $basketData[0],
"l" => $basketData[1]
),
"mySessionCheck" => $_SESSION['mySessionCheck']
)
);
Если это сообщение меняется одновременно с корзиной, вы наверняка знаете, что это проблема с сеансами PHP.
В этом случае вы можете попробовать несколько вещей:
1) Вы делаете
$lifetime=60 * 60 * 24 * 365;
$domain = ".mywebsite.com";
session_set_cookie_params($lifetime,"/",$domain);
@session_start();
Но в соответствии с пользователь внес комментарий из документов PHP.net:
PHP Session Control неправильно обрабатывает сеансовые сеансы при использовании session_set_cookie_params()
.
Итак, вы можете попробовать вместо setcookie()
:
$lifetime=60 * 60 * 24 * 365;
session_start();
setcookie(session_name(),session_id(),time()+$lifetime);
Несмотря на то, что это 4-летняя заметка, указанная в комментариях, я тестировал ее, и это все еще происходит (я на PHP 5.5.7, Windows Server 2008, IIS/7.5). Только setcookie()
создало заголовки HTTP для изменения даты истечения срока действия (пример установки $lifetime
до 600):
Set-Cookie: PHPSESSID=(the id); expires=Mon, 22-Jun-2015 15:03:17 GMT; Max-Age=600
2) Если вы используете серверы Debian или некоторые производные, используйте задание cron для очистки сеансов PHP, чтобы вы могли попробовать:
3) Чтобы узнать, есть ли процесс очистки ваших сеансов, вы можете поместить часы в каталог, где хранятся файлы сеанса (фактический путь зависит от сервера к серверу, используйте session_save_path
, чтобы узнать местоположение на вашем). Я не администратор сервера, но я прочитал, что вы можете использовать auditctl
, просто убедитесь, что вы запишите кто внес изменения в ваши файлы.
4) Если у вас нет доступа к конфигурации сервера или вы не хотите зависеть от конфигурации сервера (хорошо, если вы переключаете узлы), вы можете реализовать свой собственный обработчик сеанса. Посмотрите этот пример Педро Гимено.
Ответ 2
Вы помещаете только @session_start();
в начало всего script.
An также помещается в верхнюю часть вашего ajax script.
Пример Пример:
@session_start();
// you may use session script here or header file
include("header.php");
//some code. you may use session script here or header file
include("main.php");
//-----------next code