php会话随机丢失,无法理解为啥
Posted
技术标签:
【中文标题】php会话随机丢失,无法理解为啥【英文标题】:php session is randomly lost and cant understand whyphp会话随机丢失,无法理解为什么 【发布时间】:2015-09-05 00:36:15 【问题描述】:我花钱请一位程序员制作购物篮脚本以使用 Spreadshirt API。一切都运行良好,只是篮子一直在自己倒空。我认为会话在某些时候会丢失,因此脚本会创建另一个 BasketId
。
我试图找出它发生的具体原因,但没有任何成功......我无法重现该错误。它只是随机发生,没有任何理由。关闭浏览器、重置 apache 甚至整个网络服务器都不会导致会话丢失。
我有两个不同的脚本在同一个域上使用 cookie,它们没有任何问题(一个是管理员登录会话的 cookie,另一个 cookie 是保存用户在商店中最后查看的文章)
我尝试了在谷歌上找到的所有解决方案,但没有成功:编辑php.ini
,通过php强制设置ini,尝试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="http://api.spreadshirt.'.$config['ShopSource'].'/api/v1/shops/' . $data['shopId'] . '/articles/' . $data['articleId'] . '">
<properties>
<property key="appearance">' . $data['appearance'] . '</property>
<property key="size">' . $data['size'] . '</property>
</properties>
</element>
<links>
<link type="edit" xlink:href="http://' . $data['shopId'] .'.spreadshirt.' .$config['ShopSource'].'/-A' . $data['articleId'] . '"/>
<link type="continueShopping" xlink:href="http://' . $data['shopId'].'.spreadshirt.'.$config['ShopSource'].'"/>
</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;
?>
脚本还有 2 个其他部分:一个将样品 T 恤添加到购物篮的表单(example.php)
和一个调用 ajax 的脚本(shop-controller.js)
。如果需要可以发布,但没有会话处理的东西。
更新 - 问题可能与会话无关。 BasketId 丢失,但 PHPSESSID 在浏览器 cookie 中保持不变。
过去 3 天我做了以下测试(用不同的计算机和浏览器测试):
清空浏览器 cookie,然后在下午开始新会话
将 1 件商品添加到购物篮,我记下 BasketId 并检查浏览器 cookie 以记下 PHPSESSID
通常总是在午夜左右,篮子自己倒空
PHPSESSID 在我的浏览器 cookie 中保持不变,即使篮子自己清空了
但是BASKETID不一样,下午用的那个丢了,重新生成一个新的
服务器是 CentOS 5.9 - PHP 版本 5.2.9(来自 OVH)。专用 IP 上的专用服务器。
【问题讨论】:
您是否尝试在php.ini
中将session.auto_start
设置为on
?
@tftd 他正在使用session_set_cookie_params()
,需要在调用session_start()
之前调用它,所以也许它不是一个选项,除了它有一些缺点(例如here和@987654323) @)。
没错。我只是建议它,因为我们这里没有完整的代码——只有一部分被称为上帝知道什么时候的 ajax 脚本。开发人员可能忘记在其中一个脚本中设置它,这就是我建议它的原因。
服务器托管在哪里?如果是共享主机,是哪一个?如果自托管,它在哪里,服务器上是否有任何 crons?如果云上的虚拟机(aws/rackspace/etc),是哪一个,是否有任何 crons?我只是想知道服务器是否发生了与 php 代码或会话无关的其他事情。
我在此处发布了与此脚本相关的另外 2 个文件:board.phpbuilder.com/…
【参考方案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 文档的user contributed note:
使用
session_set_cookie_params()
时,PHP 的会话控制无法正确处理会话生命周期。
所以你可以尝试使用setcookie()
代替:
$lifetime=60 * 60 * 24 * 365;
session_start();
setcookie(session_name(),session_id(),time()+$lifetime);
尽管它是 cmets 中指出的 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 服务器或一些衍生服务器,他们use a cron job to clear out PHP sessions,所以你可以试试:
Increasing server's configuredmaxlifetime
;
Saving your sessions somewhere else;
使用memcached。
3) 要查看是否有某个进程清除了您的会话,您可以在存储会话文件的目录上进行观察(实际路径因服务器而异,请使用session_save_path
查找你的)。我不是服务器管理员,但我读过你可以使用auditctl
,只要确保你登录who made the changes to your files。
4) 如果您无权访问服务器配置,或者不想依赖服务器配置(如果您切换主机就好了),您可以实现自己的会话处理程序。看看这个example by Pedro Gimeno。
【讨论】:
该用户提供的笔记已有四年历史了,我怀疑那里所说的是否仍然适用……如果确实如此;我从来没有遇到过 PHP 自己更新会话 cookie 生命周期的任何此类问题。 @CBroe 同意,这是来自 PHP 文档的旧评论。由于 OP 从未说过他正在运行什么版本的 PHP,我会等到他尝试它,如果他没有成功,我很乐意查看我的答案以改进它或删除它。 服务器是 CentOS 5.9 - PHP 版本 5.2.9 查看我第一篇文章的更新,如果我检查浏览器 cookie,似乎会话没有丢失,但购物篮仍然是空的 如果会话丢失或随机重新生成,那么用户的登录状态不应该也不会丢失吗?我的意思是,如果由于会话丢失或未设置而生成购物篮 ID,那么该用户如何保持登录状态?【参考方案2】:您只将 @session_start();
放在所有脚本的顶部。
一个也放在你的 ajax 脚本的顶部。
示例如下:
@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
【讨论】:
【参考方案3】:我在这里发帖,即使是旧帖,万一有人遇到这个问题,检查php.ini session.gc_maxlifetime,或者打印ini_get('session.gc_maxlifetime');您必须在您的 php 脚本或 php.ini 中设置它,在我的 php 版本上,默认值为 1440 秒,我已将其更改为 1 个月,在我的情况下就足够了。 同样在开始会话后,您可以 setcookie(session_name(),session_id(),time() + $sessionLifetime, "", "", false, true); 我希望这会有所帮助。
【讨论】:
以上是关于php会话随机丢失,无法理解为啥的主要内容,如果未能解决你的问题,请参考以下文章
为啥这个 PHP 脚本(由 AJAX 调用)随机无法正确加载 SESSION?
为啥我使用 PHP 的 $_SERVER 变量得到一个随机字符串作为 IP 地址?