<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; session</title>
	<atom:link href="https://www.phppan.com/tag/session/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sun, 10 May 2026 02:26:45 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>WEB 会话管理杂谈</title>
		<link>https://www.phppan.com/2022/01/web-session/</link>
		<comments>https://www.phppan.com/2022/01/web-session/#comments</comments>
		<pubDate>Sun, 16 Jan 2022 04:29:18 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[OAuth2.0]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[WEB应用]]></category>
		<category><![CDATA[会话管理]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=2012</guid>
		<description><![CDATA[1 前言 某一天，有同事在产品问题反馈大群里扔出一个问题：登录后隔两天再打开页面就需要重新登录。在查问题的过程 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1 id="1-前言" style="color: #404040;">1 前言</h1>
<p style="color: #404040;">某一天，有同事在产品问题反馈大群里扔出一个问题：登录后隔两天再打开页面就需要重新登录。在查问题的过程中，我们有提到 7 天过期，老板对此也扔出了一个问题：为什么是 7 天。于是有了今天这篇文章。首先我们先看一下 WEB 会话管理的概念。</p>
<h1 id="2-概念" style="color: #404040;">2 概念</h1>
<p style="color: #404040;">在说 WEB 会话管理之前，我们需要先说一下 HTTP 协议。这里我们暂且不提 HTTP2.0 和 HTTP3.0，本文的 HTTP 协议主要是指现在使用最广泛，使用历史最久的 HTTP1.x 版本。</p>
<p style="color: #404040;">HTTP 协议是一个无连接无状态的协议。</p>
<ul style="color: #404040;">
<li>无连接是指限制每次连接只处理一个请求，服务端处理完客户端的请求，并收到客户端的应答后，即断开连接。后面协议中增加了 Keep-Alive，这个特性使得客户端到服务器端的连接持续有效，当出现对服务端的后继请求时，Keep-Alive 功能避免了建立或者重新建立连接。</li>
<li>无状态是指在协议层面对于事务处理没有记忆能力，服务端不知道客户端是什么状态。当我们给服务器发送 HTTP 请求之后，服务端根据请求内容给我们返回数据，但是返回数据后，服务端不会记录任何信息。</li>
</ul>
<p style="color: #404040;">在互联网早期，这样的协议完全能满足 WEB 应用的需要，但随着 WEB 应用复杂度的提升，识别用户是谁，记录用户的行为，有用户状态变成了刚性需求，或者说是核心功能点。 此时，会话管理应运而生，会话管理是一种控制和维持用户交互状态的机制，它定义了一系列用于管理用户和 WEB 应用系统交互状态的措施。</p>
<p style="color: #404040;">会话管理有两个关键点，一个是怎样存储，另一个是存储什么。</p>
<h1 id="3-存储方式" style="color: #404040;">3 存储方式</h1>
<h2 id="31-服务端-session" style="color: #404040;">3.1 服务端 session</h2>
<p style="color: #404040;">在早期的有会话管理的 WEB 应用中，会话经常存储在服务端的 session 中，常见的 WEB 编程语言都自带 session 的处理逻辑，如 PHP 和 Java 都默认带有 session 的扩展或组件，如 PHP 中的全局变量 $_SESSION。客户端存储 sessionid，通过 cookie / 隐藏的表单字段 / 重写 URL 的方式传递 sessionid，后两种方式一般在禁用 cookie 的时候启用。</p>
<p style="color: #404040;"><span style="font-weight: bold;">服务端 seesion 的优势</span></p>
<ul style="color: #404040;">
<li>安全性好，客户端与服务端保持会话状态的媒介始终只是一个 sessionid 串，我们一般会保证这个串的随机性，以防止攻击者遍历或穷举；除非通过 CSRF 或 HTTP 劫持等方式，才有可能冒充别人进行操作。最坏的情况就算是冒充成功，在服务端我们也会有一些登录凭证的验证来进行纵深的防御；</li>
<li>可控度高，在服务端集中存储，后台同学可以对其进行操作，比如踢下线之类的操作。</li>
</ul>
<p style="color: #404040;">针对服务端的存储方式，就会出现了我们在前言中提到的过期问题，为什么要有过期？</p>
<ol style="color: #404040;">
<li>为了节省服务器资源，登录态相关信息存储在服务器还是需要一定资源的，如果一直不过期，几千万上亿的用户登录态数据将是较大的一个成本，而这个成本是完全可以省下来的；</li>
<li>为了安全，用户长时间未操作，如果有人用了他的账号干了点别的，这样就存在较大的风险；另外在业务逻辑上可以实现抢登等逻辑以规避一些安全上的风险。</li>
</ol>
<p style="color: #404040;">有了过期，就会有过期时间，常见的过期时间有 1 小时（ 3600 秒），2 小时( 7200 秒)，1 天， 7 天（一周），15 天，30 天（一个月）。这个过期时间更多的是经验值，或者说是一个大家认为合适的值。这个值对业务的影响，貌似没有一个客观的评价。</p>
<p style="color: #404040;">服务端的 seesion 的常见存储方案如下：</p>
<ul style="color: #404040;">
<li>单机方案，存储要么是硬盘，要么是内存，二者的区别在于获取和写入的速度，对于海量的互联网应用来说，存储到内存方案更常见一些。单机存储方案是有一定上限的，其上限是单机存储的上限。</li>
<li>分布式方案，分布式的内存数据库如 Redis / Memecached，Tomcat 等都有现成的共享 session 方案。分布式存储方案从理论上来讲是没有存储上限的，可以给 Redis 做分布式部署。</li>
</ul>
<h2 id="32-客户端-cookie-方案" style="color: #404040;">3.2 客户端 cookie 方案</h2>
<p style="color: #404040;">考虑到服务器的负担和架构的复杂性，依赖于浏览器每次请求都会带上 cookie 的特性，我们可以把用户的登录凭证直接存到客户端（浏览器）中。当用户登录成功之后，把登录凭证写到 cookie 里面，并给 cookie 设置有效期，后续请求直接验证存有登录凭证的 cookie 是否存在以及凭证是否有效，即可判断用户的登录状态。当年 Rails 的默认会话存储方案是用 cookie 方案。</p>
<p style="color: #404040;"><span style="font-weight: bold;">优点</span></p>
<ul style="color: #404040;">
<li>实现了服务端的无状态化，彻底移除了服务端对会话管理的逻辑，服务端只负责创建和验证登录 cookie 即可；</li>
<li>不同应用间的登录态可以通过算法或密钥的一致性来保持，以实现会话共享。</li>
</ul>
<p style="color: #404040;"><span style="font-weight: bold;">缺点</span></p>
<ul style="color: #404040;">
<li>cookie 有大小限制，存储不了太多数据，同时会话会占用其它业务场景的空间，从而导致 cookie 空间紧张；</li>
<li>cookie 在每次请求时都会带上，当会话中有较多的冗余数据时，会对性能有一些影响，并且产生一些不必要的网络带宽；</li>
<li>多应用时可能会有跨域问题，浏览器在发起请求时会检查所有存储的 cookie ，如果某个 cookie 所声明的作用范围大于等于将要请求的资源所在的位置，则会把该 cookie 附在请求资源的 HTTP 请求头上发送给服务器。当作用范围小了，或者交错了，则会出现跨域问题。</li>
</ul>
<h2 id="33-令牌方案" style="color: #404040;">3.3 令牌方案</h2>
<p style="color: #404040;">相对于前面两种方案，令牌方案是前后端分离后的常见方案，session 的概念在这个方案里面更淡一些。</p>
<p style="color: #404040;">在讲令牌方案之前我们先讲一下安全上两个非常重要的点：认证和授权。有时候会把这两个东西弄混，其定义如下：</p>
<ul style="color: #404040;">
<li>认证是这样一种验证过程：通过让用户、网站、应用程序通过提供合法证书或验证方式，以证明他们符合自己所宣称的身份。</li>
<li>授权是指的是一个验证某用户能访问什么的过程。在授权过程中，某用户 / 应用程序的权限级别被确定后，才被允许访问特定的 APIs / 模块。通常，授权发生在用户身份被认证之后。</li>
</ul>
<div class="table-responsive" style="color: #404040;">
<table class="table">
<thead>
<tr>
<th>区别</th>
<th>认证</th>
<th>授权</th>
</tr>
</thead>
<tbody>
<tr>
<td>作用</td>
<td>确定用户所宣称的身份</td>
<td>确定用户可访问的权限</td>
</tr>
<tr>
<td>方式</td>
<td>通过合法凭证校验用户</td>
<td>通过规则和策略校验访问</td>
</tr>
<tr>
<td>时机</td>
<td>早于授权</td>
<td>在认证成功后执行</td>
</tr>
<tr>
<td>实现</td>
<td>通过 ID tokens 实现</td>
<td>用 Access Tokens 实现</td>
</tr>
</tbody>
</table>
</div>
<p style="color: #404040;">基于令牌的认证和授权是现在最流行的的一种技术，当用户在某处输入一次其用户名和密码后，作为交换会得到一个唯一生成的已加密令牌。该令牌随后会替代登陆凭证，用以访问受保护的页面或资源。当我们要进行下一步的业务操作，会通过令牌获取到用户的的信息和会话的信息，此时一般是通过进程内的缓存或者某个特定的微服务来获取，这些微服务的后端的存储因公司技术架构和服务而异。一般来说，对于海量的互联网应用来说，这里最终还是从内存中获取的数据，如前面说到的分布式 NoSQL 数据库。</p>
<p style="color: #404040;">一个令牌就是服务器生成的一段数据，包含了唯一性识别一个用户的信息，一般被生成为一长串随机字符和数字。</p>
<p style="color: #404040;">比如看起来可能像这样：bb74324734bcf34748bb08bu2842f3288 或更复杂些比如： eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ1bXMiLCJzdWIiOjY1NDA1NjI5NCwiYXVkIjoiZ2FvZGluZ3giLCJleHAiOjE2NDE5MDgzOTZ9.8MZaB55vlScSDZxBmO-N9ol9UTvYPVvgUFab7dFe6fY 这个令牌本身是无意义和无用的，但结合适当的令牌系统，就会变成保证应用安全性的重要一环。</p>
<p style="color: #404040;">基于令牌的实现过程一般如下：</p>
<ol style="color: #404040;">
<li>用户通过用户名和密码（或者其它登录方式，如国内的微信扫码）请求访问；</li>
<li>应用验证登录凭证；</li>
<li>应用向客户端发放已签名的令牌；</li>
<li>客户端存储令牌，并将其附加在其后的每次请求中一同发送（一般是带在 cookie 中，和 sessionid 类似）；</li>
<li>服务器验证令牌并响应数据。</li>
</ol>
<p style="color: #404040;">我们常用的基于令牌的方案有两个:</p>
<ul style="color: #404040;">
<li>OAuth 2.0 (<a style="color: #337ab7;" href="https://datatracker.ietf.org/doc/html/rfc6749">RFC 6749</a> and <a style="color: #337ab7;" href="https://tools.ietf.org/html/rfc6750">RFC 6750</a>).</li>
<li>JWT (<a style="color: #337ab7;" href="https://tools.ietf.org/html/rfc7519">RFC 7519</a>).</li>
</ul>
<p style="color: #404040;">关于这两个方案的更详细的说明见：<a style="color: #337ab7;" href="https://juejin.cn/post/6844904000811171847">深入 OAuth2.0 和 JWT</a></p>
<p style="color: #404040;">在令牌的生命周期中存在两个较大的安全薄弱环节：</p>
<ol style="color: #404040;">
<li>会话令牌生成过程中的薄弱环节，令牌的生成依赖于用户通过用户名/邮箱或密码生成，这里存在较大的泄漏风险；</li>
<li>所有处理会话令牌的薄弱环节，如传输过程中（没有加密的链路或被劫持等），日志记录中等。</li>
</ol>
<h1 id="4-存储内容" style="color: #404040;">4 存储内容</h1>
<p style="color: #404040;">会话从本质上来讲是一种缓存，一种强关联用户的缓存，所以与用户相关的一些常用数据可以放在会话里面，如头像、用户名、真实姓名、积分等等。这些缓存数据都只读，当要更新时还是需要从数据库中获取后再次更新写入，而不是依赖于会话的数据。</p>
<p style="color: #404040;">除了常用数据我们经常还会把购物车或者菜单、权限等写入会话中，此时除了用户属性，还带上了业务属性，我们可以称之为业务会话。业务会话一般是指某个业务场景中需要临时缓存起来的数据，而且这部分数据大多数是要落库的，当用户会话过期重新登录后这部分数据还是要从数据库重新获取，加载到会话中。</p>
<h1 id="5-小结" style="color: #404040;">5 小结</h1>
<p style="color: #404040;">最后这个问题的原因是业务逻辑里面针对不同的渠道有抢登的业务逻辑，而这个逻辑在某个时候是不需要触发的。 此外，老板最后在群里问了一个触及灵魂的问题：</p>
<p style="color: #404040;"><span style="font-weight: bold;">我们做一件事情的时候是依着惯性做事，还是基于深度思考后的决策？</span></p>
<p style="color: #404040;">自我反省中~~</p>
<p style="color: #404040;">这篇文章墨迹了几周，一直不知道如何写好，总觉得写得有点不如人意，如果往协议本身来写，好像也没有想写的，就这样吧。</p>
<h1 id="6-参考资料" style="color: #404040;">6 参考资料</h1>
<ul style="color: #404040;">
<li>https://juejin.cn/post/6844904000811171847</li>
<li>https://www.cnblogs.com/lyzg/p/6067766.HTML</li>
<li>https://blog.csdn.net/tennysonsky/article/details/44562435</li>
</ul>
<hr />
<p class="p2">
<p class="p2"><span class="s1">除了眼前的苟且,还有架构与远方。</span></p>
<p class="p2"><span class="s1">介绍创业路上的技术选型和架构、大型网站架构、高性能高可用可扩展架构实现，技术管理等相关话题，紧跟业界主流步伐。</span></p>
<p class="p2"><a style="color: #0f3647;" href="http://www.phppan.com/wp-content/uploads/2016/09/qrcode_for_gh_5d3f534e15fe_344-1.jpg"><img class="aligncenter size-full wp-image-1959" src="http://www.phppan.com/wp-content/uploads/2016/09/qrcode_for_gh_5d3f534e15fe_344-1.jpg" alt="qrcode_for_gh_5d3f534e15fe_344 (1)" width="344" height="344" /></a></p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2022/01/web-session/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>关于Cookie</title>
		<link>https://www.phppan.com/2013/03/about-cookie/</link>
		<comments>https://www.phppan.com/2013/03/about-cookie/#comments</comments>
		<pubDate>Sat, 30 Mar 2013 00:17:15 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[程序相关]]></category>
		<category><![CDATA[Cookie]]></category>
		<category><![CDATA[Cookies]]></category>
		<category><![CDATA[RFC]]></category>
		<category><![CDATA[RFC2965]]></category>
		<category><![CDATA[session]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1808</guid>
		<description><![CDATA[Cookie是什么 在wiki中Cookie的定义为: Cookie（复数形态Cookies），中文名称为小型 [&#8230;]]]></description>
				<content:encoded><![CDATA[<h2>Cookie是什么</h2>
<p>在wiki中Cookie的定义为:  Cookie（复数形态Cookies），中文名称为小型文本文件或小甜饼（貌似这只是一个中文翻译，平时还是直接读的英文），指某些网站为了辨别用户身份而储存在用户本地终端上的数据。</p>
<p>Cookie是服务器在本地机器上存储的小段文本并随每一个请求发送至同一个服务器，是客户端与服务器保持会话的主要手段，其内容总是保存在客户端中，按在客户端中的存储位置，可分为内存Cookie和硬盘Cookie。内存Cookie由浏览器维护，保存在内存中，浏览器关闭后就消失了，其存在时间是短暂的。硬盘Cookie保存在硬盘里，有一个过期时间，除非用户手工清理或到了过期时间，硬盘Cookie不会被删除，其存在时间是长期的。所以，按存在时间，可分为非持久Cookie和持久Cookie。</p>
<p>Cookie被浏览器默认发送到服务器，通过HTTP协议，请求头中以Cookie字段存储客户端的Cookie值，应答头中以Set-Cookie字段应答，<strong>当服务器需要有多个cookie字段写到客户端，则在应答头中将包含多个Set-Cookie字段</strong>。  Cookie的使用非常简单，以PHP为例，在脚本中使用setcookie函数设置对应的key,value值，通过全局变量$_COOKIE直接读取客户端发送过来的Cookie值。</p>
<p>Cookie简单，但是存在一些问题:</p>
<ol>
<li>安全，明文传输内容，容易被篡改。和HTTP一样，只能说看如何使用了，看你存储的是什么了</li>
<li>增加网络流量，加重整个网络的负载。默认浏览器在发送请求时会将本地Cookie的内容通过Cookie字段传输到服务器。所以经常我们会独立静态图片或资源的域名，使其Cookie为空。</li>
<li>大小限制。各浏览器对于单个cookie的大小限制为4096个字节左右，超过大小的内容将被忽略。每个域名下可以存储有30~50个cookie，不同的浏览器，不同的版本这些值不同。为什么会有大小限制，因为cookie会默认发送，当cookie太大时，可能会导致服务器响应出错等。</li>
</ol>
<h2>Cookie的历史</h2>
<p>1993年3月，这样一个春光明媚，面朝大海，春暖花开的时节，现在的网景公司前雇员，当时的NB的网景公司员工Lou  Montulli灵光一闪，Cookie华丽丽的出生了。 Cookie第一次被正式定义是在RFC2109，嗯，这是1997年2月的一天，也许那时还有些冷。在RFC中，Cookie被称为HTTP State Management Mechanism（HTTP 状态管理机制）。  RFC2109在2000年10月被RFC2965过时，而在2011年4月，最新的刚刚火热出炉的RFC6265将RFC2965过时，可谓是长江后浪推前浪，前浪死在沙滩上。另外，RFC2964记录了使用Cookie的最佳实践。</p>
<p>换句话说:Cookie经过了Netscape标准、RFC2109、RFC2965和RFC26265四个标准：</p>
<ul>
<li>Netscape标准：Netscape是最原始的Cookies规范，同时也是RFC2109的基础。尽管如此，还是在很多重要的方面与RFC2109不同，可能需要特定服务器才可以兼容。</li>
<li>RFC2109：  RFC2109是W3C组织第一次推出的官方Cookies标准。理论上，所有使用版本Cookies的服务端都应该使用此标准。HttpClient已经将此标准设定为默认。遗憾的是，许多服务端不正确的实现了标准或者仍然使用Netscape标准。所有有时感到此标准太多于严格。</li>
<li>RFC2965：RFC2965定义了版本2并且尝试去弥补在版本1中Cookie的RFC2109标准的缺点。RFC2965是，并规定RFC2965最终取代RFC2109.  发送RFC2965标准Cookies的服务端，将会使用Set-Cookie2 header添加到Set-Cookie Header信心中，RFC2965  Cookies是区分端口的。</li>
<li>RFC6265：RFC6265主要是干掉了RFC2965，在9.3和9.4小节。另外，增加了HttpOnly字段，指定HttpOnly的Cookie不能被客户端读写，仅供HTTP传输使用，或者就服务器可以读写，浏览器作为客户端需要确保其不能读写。</li>
</ul>
<h2>Cookie和Seesion</h2>
<p>Cookie和Session都用来保存状态信息，做会话处理，都是保存客户端状态的机制，它们都是为了解决HTTP无状态的问题而所做的努力。  Session存储在服务器，一般通过Cookie来存储其生成的唯一ID（seesionID），当Cookie被禁用时，通常用URL回写的机制来替换Cookie。</p>
<p><strong>Cookie和Session有一些不同：</strong></p>
<ol>
<li>存储位置的不同：Cookie将状态保存在客户端，Session将状态保存在服务器端；</li>
<li>与HTTP协议的关系不同：Cookie需要通过网络传输，依赖于HTTP协议，Session并没有在HTTP的协议中定 义；</li>
<li>可用性不同：相对于Cookie，Session在客户端禁用Cookie后还可以通过URL回写机制实现Session会话机制。</li>
<li>安全性不同：因为存储的位置不同，Cookie更容易被篡改，存储在服务器的Session相对来说则安全一些，客户不能随意读取这些内容，除非获到其它用户的了sessionID，这也是XSS攻击会关注的地方。</li>
</ol>
<h2>同源策略</h2>
<p>说到WEB的安全问题就不得不提同源策略。浏览器的同源策略是 Web  安全的基础，所有的主流浏览器都会有相应的实现。同源策略中“源”是一个包含主机名、协议和端口号的三元组，则同源表示：同协议，同域名和同端口，三者都相同。同源策略的出发点是它认为自任何站点装载的信赖内容是不安全的。在同源策略的限制下，浏览器只允许网页中的脚本（如  JavaScript 或 VBScript）访问与之同源的 HTTP 请求和  Cookie。对于Cookie来说，同源策略就限制了网站间的Cookie读写操作。即使在服务器使用setcookie（PHP）函数对其它域名执行Cookie写操作也是无效的。  setcookie的域名是用来指向当前域名或根域名之类的用的,设置Cookie时，如果不指定domain的值，默认就是本域。</p>
<h2>参考资料：</h2>
<ol>
<li>http://wiki.apache.org/HttpComponents/ReferenceMaterials</li>
<li>http://www.cnblogs.com/shepherd2012/archive/2012/08/03/2621797.html</li>
<li>http://zh.wikipedia.org/wiki/Cookie</li>
<li>http://curl.haxx.se/rfc/cookie_spec.html</li>
<li>http://tools.ietf.org/html/rfc6265</li>
</ol>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2013/03/about-cookie/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP源码阅读笔记三十七：PHP中的SESSION实现</title>
		<link>https://www.phppan.com/2010/12/php-source-code-37-session-cookie-cache-serialize/</link>
		<comments>https://www.phppan.com/2010/12/php-source-code-37-session-cookie-cache-serialize/#comments</comments>
		<pubDate>Thu, 09 Dec 2010 00:55:17 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[session]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1165</guid>
		<description><![CDATA[PHP源码阅读笔记三十七：PHP中的SESSION实现 源码版本：php5.3.1 环境:VS2008 本文包 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>PHP源码阅读笔记三十七：PHP中的SESSION实现<br />
源码版本：php5.3.1<br />
环境:VS2008<br />
本文包括PHP中SESSION用到的COOKIE管理，缓存限制，序列化<br />
<strong>【COOKIE管理】</strong><br />
在浏览器未关闭cookie的情况下，我们可以看到当有session id生成并返回给发送请求的客户端时，会有一些cookie信息写入到浏览器。其实现代码在session.c 1191行开始。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* *********************
   * Cookie Management *
   ********************* */</span>
&nbsp;
<span style="color: #339933;">#define COOKIE_SET_COOKIE &quot;Set-Cookie: &quot;</span>
<span style="color: #339933;">#define COOKIE_EXPIRES	&quot;; expires=&quot;</span>
<span style="color: #339933;">#define COOKIE_PATH	&quot;; path=&quot;</span>
<span style="color: #339933;">#define COOKIE_DOMAIN	&quot;; domain=&quot;</span>
<span style="color: #339933;">#define COOKIE_SECURE	&quot;; secure&quot;</span>
<span style="color: #339933;">#define COOKIE_HTTPONLY	&quot;; HttpOnly&quot;</span>
&nbsp;
<span style="color: #993333;">static</span> <span style="color: #993333;">void</span> php_session_send_cookie<span style="color: #009900;">&#40;</span>TSRMLS_D<span style="color: #009900;">&#41;</span> <span style="color: #808080; font-style: italic;">/* {{{ */</span>
<span style="color: #009900;">&#123;</span>
	smart_str ncookie <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>date_fmt <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>e_session_name<span style="color: #339933;">,</span> <span style="color: #339933;">*</span>e_id<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>SG<span style="color: #009900;">&#40;</span>headers_sent<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>output_start_filename <span style="color: #339933;">=</span> php_get_output_start_filename<span style="color: #009900;">&#40;</span>TSRMLS_C<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #993333;">int</span> output_start_lineno <span style="color: #339933;">=</span> php_get_output_start_lineno<span style="color: #009900;">&#40;</span>TSRMLS_C<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>output_start_filename<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			php_error_docref<span style="color: #009900;">&#40;</span>NULL TSRMLS_CC<span style="color: #339933;">,</span> E_WARNING<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Cannot send session cookie - headers already sent by (output started at %s:%d)&quot;</span><span style="color: #339933;">,</span> output_start_filename<span style="color: #339933;">,</span> output_start_lineno<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span> <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
			php_error_docref<span style="color: #009900;">&#40;</span>NULL TSRMLS_CC<span style="color: #339933;">,</span> E_WARNING<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;Cannot send session cookie - headers already sent&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
		<span style="color: #b1b100;">return</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/* URL encode session_name and id because they might be user supplied */</span>
	e_session_name <span style="color: #339933;">=</span> php_url_encode<span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>session_name<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #000066;">strlen</span><span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>session_name<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	e_id <span style="color: #339933;">=</span> php_url_encode<span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>id<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #000066;">strlen</span><span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>id<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> COOKIE_SET_COOKIE<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> e_session_name<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	smart_str_appendc<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> <span style="color: #ff0000;">'='</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> e_id<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	efree<span style="color: #009900;">&#40;</span>e_session_name<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	efree<span style="color: #009900;">&#40;</span>e_id<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>cookie_lifetime<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">struct</span> timeval tv<span style="color: #339933;">;</span>
		time_t t<span style="color: #339933;">;</span>
&nbsp;
		gettimeofday<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>tv<span style="color: #339933;">,</span> NULL<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		t <span style="color: #339933;">=</span> tv.<span style="color: #202020;">tv_sec</span> <span style="color: #339933;">+</span> PS<span style="color: #009900;">&#40;</span>cookie_lifetime<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>t <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			date_fmt <span style="color: #339933;">=</span> php_format_date<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;D, d-M-Y H:i:s T&quot;</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;D, d-M-Y H:i:s T&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> t<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span> TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> COOKIE_EXPIRES<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> date_fmt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			efree<span style="color: #009900;">&#40;</span>date_fmt<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>cookie_path<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> COOKIE_PATH<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> PS<span style="color: #009900;">&#40;</span>cookie_path<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>cookie_domain<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#91;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> COOKIE_DOMAIN<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> PS<span style="color: #009900;">&#40;</span>cookie_domain<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>cookie_secure<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> COOKIE_SECURE<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>cookie_httponly<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		smart_str_appends<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #339933;">,</span> COOKIE_HTTPONLY<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	smart_str_0<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>ncookie<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #808080; font-style: italic;">/*	'replace' must be 0 here, else a previous Set-Cookie
		header, probably sent with setcookie() will be replaced! */</span>
	sapi_add_header_ex<span style="color: #009900;">&#40;</span>ncookie.<span style="color: #202020;">c</span><span style="color: #339933;">,</span> ncookie.<span style="color: #202020;">len</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span> TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span></pre></td></tr></table></div>

<p>第1195~1200行 定义常量宏，这些定义的内容我们可以通过查看http协议的应答头中可以看到，Set-Cookie是字段名，其余的为此字段中可以包含的变量，我们可以通过与PHP自带的setcookie操作函数的参数比对进行学习，此函数的定义如下:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
</pre></td><td class="code"><pre class="php" style="font-family:monospace;">bool <span style="color: #990000;">setcookie</span> <span style="color: #009900;">&#40;</span> string <span style="color: #000088;">$name</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">,</span> string <span style="color: #000088;">$value</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">,</span> int <span style="color: #000088;">$expire</span> <span style="color: #339933;">=</span> <span style="color: #cc66cc;">0</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">,</span> string <span style="color: #000088;">$path</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">,</span> string <span style="color: #000088;">$domain</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">,</span> bool <span style="color: #000088;">$secure</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">false</span> <span style="color: #009900;">&#91;</span><span style="color: #339933;">,</span> bool <span style="color: #000088;">$httponly</span> <span style="color: #339933;">=</span> <span style="color: #009900; font-weight: bold;">false</span> <span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#93;</span> <span style="color: #009900;">&#41;</span></pre></td></tr></table></div>

<p>相关参数说明请参考：http://docs.php.net/manual/zh/function.setcookie.php<br />
第1208~1218行 关于是否已经发送了cookie应答的情况处理<br />
第1221~1222行 使用PHP中的函数urlencode的C程序实现，以防止人为添加的session name和session id出现混乱<br />
第1224~1227行 将Set-Cookie字段名，session name和session id的值添加到将要发送的cookie字符串中。<br />
第1232~1245行 cookie的过期时间处理<br />
第1247~1250行 cookie的路径处理<br />
第1252~1255行 cookie的域名处理<br />
第1257~1259行 cookie是否仅仅通过安全连接(https)发送cookie。<br />
第1261~1263行 cookie是否在cookie中添加httpOnly标志(仅允许HTTP协议访问)的处理<br />
最后通过sapi_add_header_ex函数将生成的字符串添加到应答头中。<br />
<strong>【缓存限制器】</strong><br />
<strong>缓存限制器的结构定义：</strong></p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1041
1042
1043
1044
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>name<span style="color: #339933;">;</span>	
	<span style="color: #993333;">void</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>func<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>TSRMLS_D<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> php_session_cache_limiter_t<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>如上所示代码，一个限制器包括一个名称和一个处理函数。<br />
<strong>缓存限制器的实现原理</strong><br />
从其代码实现看，所谓的缓存限制器是通过返回不同的http请求的缓存控制字段内容达到缓存控制的目的。<br />
默认情况下，session.cache_limiter = nocache<br />
即使用CACHE_LIMITER_FUNC(nocache)<br />
其中关于缓存控制器总共有4种方案，分别为：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1154
1155
1156
1157
1158
1159
1160
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">static</span> php_session_cache_limiter_t php_session_cache_limiters<span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
	CACHE_LIMITER_ENTRY<span style="color: #009900;">&#40;</span>public<span style="color: #009900;">&#41;</span>
	CACHE_LIMITER_ENTRY<span style="color: #009900;">&#40;</span>private<span style="color: #009900;">&#41;</span>
	CACHE_LIMITER_ENTRY<span style="color: #009900;">&#40;</span>private_no_expire<span style="color: #009900;">&#41;</span>
	CACHE_LIMITER_ENTRY<span style="color: #009900;">&#40;</span>nocache<span style="color: #009900;">&#41;</span>
	<span style="color: #009900;">&#123;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>其调用代码在session.c1180 行，如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1180
1181
1182
1183
1184
1185
</pre></td><td class="code"><pre class="c" style="font-family:monospace;">	<span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>lim <span style="color: #339933;">=</span> php_session_cache_limiters<span style="color: #339933;">;</span> lim<span style="color: #339933;">-&gt;</span>name<span style="color: #339933;">;</span> lim<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>strcasecmp<span style="color: #009900;">&#40;</span>lim<span style="color: #339933;">-&gt;</span>name<span style="color: #339933;">,</span> PS<span style="color: #009900;">&#40;</span>cache_limiter<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			lim<span style="color: #339933;">-&gt;</span>func<span style="color: #009900;">&#40;</span>TSRMLS_C<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>以上代码为一个遍历缓存控制器所在数组，并判断其方案名与PS(cache_limiter)是否一致，如果一致则执行此缓存控制，并返回。</p>
<p><strong>【序列化实现】</strong><br />
PHP的session存放的都是序列化后的数据。<br />
默认情况下使用session.serialize_handler = php<br />
在源码中默认给出了两种序列化的实现。其最多可以有11种序列化的方法，相关代码如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>982
983
984
985
986
987
988
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define MAX_SERIALIZERS 10</span>
<span style="color: #339933;">#define PREDEFINED_SERIALIZERS 2</span>
&nbsp;
<span style="color: #993333;">static</span> ps_serializer ps_serializers<span style="color: #009900;">&#91;</span>MAX_SERIALIZERS <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
	PS_SERIALIZER_ENTRY<span style="color: #009900;">&#40;</span>php<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
	PS_SERIALIZER_ENTRY<span style="color: #009900;">&#40;</span>php_binary<span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>ps_serializer结构定义如下 ：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>161
162
163
164
165
166
167
168
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define PS_SERIALIZER_ENCODE_ARGS char **newstr, int *newlen TSRMLS_DC</span>
<span style="color: #339933;">#define PS_SERIALIZER_DECODE_ARGS const char *val, int vallen TSRMLS_DC</span>
&nbsp;
<span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> ps_serializer_struct <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>name<span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>encode<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_SERIALIZER_ENCODE_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>decode<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_SERIALIZER_DECODE_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> ps_serializer<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>序列化通过调用php_session_register_serializer函数实现序列化的注册过程<br />
php_session_register_serializer函数遍历ps_serializers数组，并判断数组元素的name属性是否为NULL,如果为NULL,则将新的序列化函数添加到ps_serializers数组。</p>
<p><strong>【其它维度的文章】</strong><br />
<a href="http://www.perfgeeks.com/?p=183">PHP5 Session 浅析I</a><br />
<a href="http://www.perfgeeks.com/?p=232">PHP5 Session 浅析II</a><br />
作者从另外一个维度分析了session的一些原理，值得学习一下。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2010/12/php-source-code-37-session-cookie-cache-serialize/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP源码阅读笔记三十六：PHP中的SESSION实现之常规操作</title>
		<link>https://www.phppan.com/2010/12/php-source-code-36-session-global-init/</link>
		<comments>https://www.phppan.com/2010/12/php-source-code-36-session-global-init/#comments</comments>
		<pubDate>Tue, 07 Dec 2010 01:09:08 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[session]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1161</guid>
		<description><![CDATA[PHP源码阅读笔记三十六：PHP中的SESSION实现之常规操作 源码版本：php5.3.1 环境:VS200 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>PHP源码阅读笔记三十六：PHP中的SESSION实现之常规操作<br />
源码版本：php5.3.1<br />
环境:VS2008<br />
本笔记包括PHP中SESSION用到的全局变量，session_id的生成算法，初始化及session的清除操作<br />
<strong>【全局变量】</strong><br />
/ext/session/php_session.h 文件</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> _php_ps_globals <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>save_path<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	保存路径 </span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>session_name<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	session名称 默认为PHPSESSID 这个在cookie中会看到</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>id<span style="color: #339933;">;</span>		<span style="color: #666666; font-style: italic;">//	session ID</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>extern_referer_chk<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	请求头中的&quot;Referer&quot;字段不包含此处指定的字符串则会话ID将被视为无效</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>entropy_file<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">// 指定这里建立 session id</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>cache_limiter<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">// 可以设为{nocache,private,public},以决定 HTTP 的缓存问题</span>
	<span style="color: #993333;">long</span> entropy_length<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	 从文件中读取多少字节 </span>
	<span style="color: #993333;">long</span> cookie_lifetime<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	为按秒记的cookie的保存时间</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>cookie_path<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	cookie的有效路径</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>cookie_domain<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	cookie的有效域 </span>
	zend_bool  cookie_secure<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	是否仅仅通过安全连接(https)发送cookie。</span>
	zend_bool  cookie_httponly<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//是否在cookie中添加httpOnly标志(仅允许HTTP协议访问)</span>
	ps_module <span style="color: #339933;">*</span>mod<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	session的处理方式</span>
	<span style="color: #993333;">void</span> <span style="color: #339933;">*</span>mod_data<span style="color: #339933;">;</span>
	php_session_status session_status<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	session的状态</span>
	<span style="color: #993333;">long</span> gc_probability<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	在每次 session 初始化的时候开始的可能性。 </span>
	<span style="color: #993333;">long</span> gc_divisor<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">// 收集概率计算公式：gc_probability/gc_divisor</span>
	<span style="color: #993333;">long</span> gc_maxlifetime<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">// 在这里数字所指的秒数后，保存的数据将被视为´碎片(garbage)´并由gc 进程清理掉。 </span>
	<span style="color: #993333;">int</span> module_number<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	存储方式编号</span>
	<span style="color: #993333;">long</span> cache_expire<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	过期时间，单位为分钟 </span>
	<span style="color: #993333;">union</span> <span style="color: #009900;">&#123;</span>
		zval <span style="color: #339933;">*</span>names<span style="color: #009900;">&#91;</span><span style="color: #0000dd;">6</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
		<span style="color: #993333;">struct</span> <span style="color: #009900;">&#123;</span>
			zval <span style="color: #339933;">*</span>ps_open<span style="color: #339933;">;</span>
			zval <span style="color: #339933;">*</span>ps_close<span style="color: #339933;">;</span>
			zval <span style="color: #339933;">*</span>ps_read<span style="color: #339933;">;</span>
			zval <span style="color: #339933;">*</span>ps_write<span style="color: #339933;">;</span>
			zval <span style="color: #339933;">*</span>ps_destroy<span style="color: #339933;">;</span>
			zval <span style="color: #339933;">*</span>ps_gc<span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span> name<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span> mod_user_names<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	用户自定义函数 你懂的</span>
	zend_bool bug_compat<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* Whether to behave like PHP 4.2 and earlier */</span>
	zend_bool bug_compat_warn<span style="color: #339933;">;</span> <span style="color: #808080; font-style: italic;">/* Whether to warn about it */</span>
	<span style="color: #993333;">const</span> <span style="color: #993333;">struct</span> ps_serializer_struct <span style="color: #339933;">*</span>serializer<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	序列化处理函数</span>
	zval <span style="color: #339933;">*</span>http_session_vars<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	存放session变量</span>
	zend_bool auto_start<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	是否自动开始 默认为0</span>
	zend_bool use_cookies<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	是否使用cookies </span>
	zend_bool use_only_cookies<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	是否仅仅使用cookie在客户端保存会话ID</span>
	zend_bool use_trans_sid<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* contains the INI value of whether to use trans-sid */</span>
	zend_bool apply_trans_sid<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* whether or not to enable trans-sid for the current request */</span>
&nbsp;
	<span style="color: #993333;">long</span> hash_func<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	生成SID的散列算法。SHA-1的安全性更高一些</span>
<span style="color: #339933;">#if defined(HAVE_HASH_EXT) &amp;&amp; !defined(COMPILE_DL_HASH)</span>
	php_hash_ops <span style="color: #339933;">*</span>hash_ops<span style="color: #339933;">;</span>
<span style="color: #339933;">#endif</span>
	<span style="color: #993333;">long</span> hash_bits_per_character<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//指定在SID字符串中的每个字符内保存多少bit，</span>
	<span style="color: #993333;">int</span> send_cookie<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">//	是否发送cookie头</span>
	<span style="color: #993333;">int</span> define_sid<span style="color: #339933;">;</span>		<span style="color: #666666; font-style: italic;">//	session常量id</span>
	zend_bool invalid_session_id<span style="color: #339933;">;</span>	<span style="color: #808080; font-style: italic;">/* allows the driver to report about an invalid session id and request id regeneration */</span>
<span style="color: #009900;">&#125;</span> php_ps_globals<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p><strong>【初始化】</strong><br />
在PHP_MINIT_FUNCTION方法中，注册全局变量_SESSION，将session_status设置为php_session_none，注册ini实体<br />
[PHP_RINIT_FUNCTION方法]</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
</pre></td><td class="code"><pre class="c" style="font-family:monospace;">&nbsp;
<span style="color: #993333;">static</span> PHP_RINIT_FUNCTION<span style="color: #009900;">&#40;</span>session<span style="color: #009900;">&#41;</span> <span style="color: #808080; font-style: italic;">/* {{{ */</span>
<span style="color: #009900;">&#123;</span>
	php_rinit_session_globals<span style="color: #009900;">&#40;</span>TSRMLS_C<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>mod<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>value<span style="color: #339933;">;</span>
&nbsp;
		value <span style="color: #339933;">=</span> zend_ini_string<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;session.save_handler&quot;</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;session.save_handler&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			PS<span style="color: #009900;">&#40;</span>mod<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> _php_find_ps_module<span style="color: #009900;">&#40;</span>value TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>serializer<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">char</span> <span style="color: #339933;">*</span>value<span style="color: #339933;">;</span>
&nbsp;
		value <span style="color: #339933;">=</span> zend_ini_string<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;session.serialize_handler&quot;</span><span style="color: #339933;">,</span> <span style="color: #993333;">sizeof</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;session.serialize_handler&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>value<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			PS<span style="color: #009900;">&#40;</span>serializer<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> _php_find_ps_serializer<span style="color: #009900;">&#40;</span>value TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>mod<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> NULL <span style="color: #339933;">||</span> PS<span style="color: #009900;">&#40;</span>serializer<span style="color: #009900;">&#41;</span> <span style="color: #339933;">==</span> NULL<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #808080; font-style: italic;">/* current status is unusable */</span>
		PS<span style="color: #009900;">&#40;</span>session_status<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> php_session_disabled<span style="color: #339933;">;</span>
		<span style="color: #b1b100;">return</span> SUCCESS<span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>auto_start<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		php_session_start<span style="color: #009900;">&#40;</span>TSRMLS_C<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #009900;">&#125;</span>
&nbsp;
	<span style="color: #b1b100;">return</span> SUCCESS<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span></pre></td></tr></table></div>

<p>第2011行 调用php_rinit_session_globals函数初始化一些变量，其代码吓：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>79
80
81
82
83
84
85
86
87
88
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">/* Dispatched by RINIT and by php_session_destroy */</span>
<span style="color: #993333;">static</span> <span style="color: #000000; font-weight: bold;">inline</span> <span style="color: #993333;">void</span> php_rinit_session_globals<span style="color: #009900;">&#40;</span>TSRMLS_D<span style="color: #009900;">&#41;</span> <span style="color: #808080; font-style: italic;">/* {{{ */</span>
<span style="color: #009900;">&#123;</span>
	PS<span style="color: #009900;">&#40;</span>id<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	PS<span style="color: #009900;">&#40;</span>session_status<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> php_session_none<span style="color: #339933;">;</span>
	PS<span style="color: #009900;">&#40;</span>mod_data<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
	<span style="color: #808080; font-style: italic;">/* Do NOT init PS(mod_user_names) here! */</span>
	PS<span style="color: #009900;">&#40;</span>http_session_vars<span style="color: #009900;">&#41;</span> <span style="color: #339933;">=</span> NULL<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span></pre></td></tr></table></div>

<p>PHP_RINIT_FUNCTION第2102~2109行 如果PS(mod)存在，则跳过，否则根据session.save_handle初始化PS(mod)；<br />
第2111~2118行 如果PS(serializer)存在，则跳过，否则根据session.serialize_handler，通过_php_find_ps_serializer函数初始化PS(serializer)；<br />
第2120~2124行 处理session不可用状态<br />
第2126~2129行 根据PS(auto_start)(即php.ini中的session.auto_start)来判断是否自动开启session</p>
<p><strong>【session id的默认生成算法】</strong><br />
session.c 350行开始定义的php_session_create_id实现了session_id的默认生成算法<br />
从其实现看，session id的内容和REMOTE_ADDR、当前时间和一个随机数有关<br />
通过PS(hash_func)获取生成session id的函数，此默认为0，即MD5算法，以MD5为例，此时会初始化MD5算法(PHP_MD5Init)，把REMOTE_ADDR及当前时间组成的字符串添加(PHP_MD5Update)到算法的加密内容中<br />
然后，判断PS(entropy_length) (从文件中读取多少字节)是否大于0，如果大于0，则需要从PS(entropy_file) （指定建立 session id的文件）中读取PS(entropy_length)长的字符串，添加(PHP_MD5Update)到MD5算法的加密内容中<br />
最后执行PHP_MD5Final操作，生成session id</p>
<p><strong>【清除过期session】</strong><br />
在php.ini文件中对于session的配置项有三个是与过期session的清除有关的，它们是session.gc_probability、session.gc_divisor和session.gc_maxlifetime。其中session.gc_probability默认为1,session.gc_divisor默认为100，session.gc_maxlifetime默认为1440秒<br />
如下所示代码在session.c 1469行，为php_session_start函数的部分实现。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
</pre></td><td class="code"><pre class="c" style="font-family:monospace;">	<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>PS<span style="color: #009900;">&#40;</span>mod_data<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span> PS<span style="color: #009900;">&#40;</span>gc_probability<span style="color: #009900;">&#41;</span> <span style="color: #339933;">&gt;</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #993333;">int</span> nrdels <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
&nbsp;
		nrand <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span><span style="color: #993333;">int</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#40;</span><span style="color: #009900;">&#40;</span><span style="color: #993333;">float</span><span style="color: #009900;">&#41;</span> PS<span style="color: #009900;">&#40;</span>gc_divisor<span style="color: #009900;">&#41;</span> <span style="color: #339933;">*</span> php_combined_lcg<span style="color: #009900;">&#40;</span>TSRMLS_C<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>nrand <span style="color: #339933;">&lt;</span> PS<span style="color: #009900;">&#40;</span>gc_probability<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			PS<span style="color: #009900;">&#40;</span>mod<span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span>s_gc<span style="color: #009900;">&#40;</span><span style="color: #339933;">&amp;</span>PS<span style="color: #009900;">&#40;</span>mod_data<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> PS<span style="color: #009900;">&#40;</span>gc_maxlifetime<span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> <span style="color: #339933;">&amp;</span>nrdels TSRMLS_CC<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #339933;">#ifdef SESSION_DEBUG</span>
			<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>nrdels <span style="color: #339933;">!=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
				php_error_docref<span style="color: #009900;">&#40;</span>NULL TSRMLS_CC<span style="color: #339933;">,</span> E_NOTICE<span style="color: #339933;">,</span> <span style="color: #ff0000;">&quot;purged %d expired session objects&quot;</span><span style="color: #339933;">,</span> nrdels<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
			<span style="color: #009900;">&#125;</span>
<span style="color: #339933;">#endif</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span></pre></td></tr></table></div>

<p>以上代码段在php_session_start函数中，表示每次启动session时都会执行，只是它会依据 PS(gc_probability)和 PS(gc_divisor)以及一个生成的随机数判断是否执行垃圾清除操作。默认其概率为1/100<br />
php_combined_lcg会生成一个（0，1）的随机数<br />
PS(mod)->s_gc是根据选择的存储方式调用其定义的垃圾收集方法<br />
以files存储方式为例，PS_GC_FUNC(files)函数调用ps_files_cleanup_dir遍历指定的目录，查找文件名为&#8221;sess_&#8221;的文件（这点在mod_files.c的51行定义#define FILE_PREFIX &#8220;sess_&#8221;），通过判断当前时间与最后修改时间之差是否大于maxlifetime（即在php.ini中配置的session.gc_maxlifetime文件）来决定是否删除文件。</p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2010/12/php-source-code-36-session-global-init/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PHP源码阅读笔记三十五：PHP中的SESSION实现之多种存储方式</title>
		<link>https://www.phppan.com/2010/12/php-source-code-35-session-save-handlers/</link>
		<comments>https://www.phppan.com/2010/12/php-source-code-35-session-save-handlers/#comments</comments>
		<pubDate>Fri, 03 Dec 2010 01:02:35 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHP扩展]]></category>
		<category><![CDATA[PHP源码]]></category>
		<category><![CDATA[session]]></category>

		<guid isPermaLink="false">http://www.phppan.com/?p=1153</guid>
		<description><![CDATA[PHP源码阅读笔记三十五：PHP中的SESSION实现之多种存储方式 源码版本：php5.3.1 环境:VS2 [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>PHP源码阅读笔记三十五：PHP中的SESSION实现之多种存储方式<br />
源码版本：php5.3.1<br />
环境:VS2008</p>
<p>在php.ini中，可以看到配置项session.save_handler = files<br />
默认情况下，php.ini 中设置的 SESSION 保存方式是 files（session.save_handler = files），即使用读写文件的方式保存 SESSION 数据，而 SESSION 文件保存的目录由 session.save_path 指定，文件名以 sess_ 为前缀，后跟 SESSION ID，如：sess_sf26st2tfamqbarvnkfo2aftf2。文件中的数据即是序列化之后的 SESSION 数据了。如果访问量大，可能产生的 SESSION 文件会比较多，这时可以设置分级目录进行 SESSION 文件的保存，效率会提高很多，设置方法为：session.save_path=&#8221;N;/save_path&#8221;，N 为分级的级数，save_path 为开始目录。当写入 SESSION 数据的时候，PHP 会获取到客户端的 SESSION_ID，然后根据这个 SESSION ID 到指定的 SESSION 文件保存目录中找到相应的 SESSION 文件，不存在则创建之，最后将数据序列化之后写入文件。读取 SESSION 数据是也是类似的操作流程，对读出来的数据需要进行解序列化，生成相应的 SESSION 变量。如果需要对session文件设置mode，设置方法为：session.save_path = &#8220;N;MODE;/path&#8221;</p>
<p>同样我们可以使用session_set_save_handler函数设置session的处理方法。<br />
bool session_set_save_handler ( callback $open , callback $close , callback $read , callback $write , callback $destroy , callback $gc )<br />
那么此时可能需要思考一些问题，对于save_handler的设置是如何实现的？在内部存在怎样的结构存储这些处理方法？不同的存储方式之间是如何替换的？如果以一个扩展的方法添加，则需要实现哪些方法？这些方法在哪里添加到系统中的？</p>
<p><strong>【结构】</strong><br />
/ext/session/php_session.h文件</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define PS_OPEN_ARGS void **mod_data, const char *save_path, const char *session_name TSRMLS_DC</span>
<span style="color: #339933;">#define PS_CLOSE_ARGS void **mod_data TSRMLS_DC</span>
<span style="color: #339933;">#define PS_READ_ARGS void **mod_data, const char *key, char **val, int *vallen TSRMLS_DC</span>
<span style="color: #339933;">#define PS_WRITE_ARGS void **mod_data, const char *key, const char *val, const int vallen TSRMLS_DC</span>
<span style="color: #339933;">#define PS_DESTROY_ARGS void **mod_data, const char *key TSRMLS_DC</span>
<span style="color: #339933;">#define PS_GC_ARGS void **mod_data, int maxlifetime, int *nrdels TSRMLS_DC</span>
<span style="color: #339933;">#define PS_CREATE_SID_ARGS void **mod_data, int *newlen TSRMLS_DC</span>
&nbsp;
<span style="color: #808080; font-style: italic;">/* default create id function */</span>
PHPAPI <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>php_session_create_id<span style="color: #009900;">&#40;</span>PS_CREATE_SID_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #993333;">typedef</span> <span style="color: #993333;">struct</span> ps_module_struct <span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>s_name<span style="color: #339933;">;</span>	<span style="color: #666666; font-style: italic;">// session存储方式的名字 如默认的files</span>
	<span style="color: #993333;">int</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>s_open<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_OPEN_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>s_close<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_CLOSE_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>s_read<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_READ_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>s_write<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_WRITE_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>s_destroy<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_DESTROY_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>s_gc<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_GC_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">char</span> <span style="color: #339933;">*</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">*</span>s_create_sid<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span>PS_CREATE_SID_ARGS<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span> ps_module<span style="color: #339933;">;</span></pre></td></tr></table></div>

<p>第43到52行 定义ps_module，用于存放整个php_session结构，通过方法名应该可以很容易的识别出其作用意图。<br />
<strong>【以扩展方式支持session】</strong><br />
如果一个扩展模块需要支持session，则在其PHP_MINIT_FUNCTION方法中调用php_session_register_module方法。<br />
如在/ext/sqlite/sqlite.c 1395行，其调用了php_session_register_module(ps_sqlite_ptr);，说明在此扩展模块初始化时已经将其作为一个session的存储方式加载到内存中了。并且在此扩展中有sess_sqlite.c文件专门处理关于session中的应该定义的各方法，其分别以宏PS_OPEN_FUNC、PS_CLOSE_FUNC、PS_READ_FUNC、PS_WRITE_FUNC、PS_DESTROY_FUNC、PS_GC_FUNC、PS_CREATE_SID_FUNC等给出。这些宏的定义如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>57
58
59
60
61
62
63
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define PS_OPEN_FUNC(x) 	int ps_open_##x(PS_OPEN_ARGS)</span>
<span style="color: #339933;">#define PS_CLOSE_FUNC(x) 	int ps_close_##x(PS_CLOSE_ARGS)</span>
<span style="color: #339933;">#define PS_READ_FUNC(x) 	int ps_read_##x(PS_READ_ARGS)</span>
<span style="color: #339933;">#define PS_WRITE_FUNC(x) 	int ps_write_##x(PS_WRITE_ARGS)</span>
<span style="color: #339933;">#define PS_DESTROY_FUNC(x) 	int ps_delete_##x(PS_DESTROY_ARGS)</span>
<span style="color: #339933;">#define PS_GC_FUNC(x) 		int ps_gc_##x(PS_GC_ARGS)</span>
<span style="color: #339933;">#define PS_CREATE_SID_FUNC(x)	char *ps_create_sid_##x(PS_CREATE_SID_ARGS)</span></pre></td></tr></table></div>

<p>php_session_register_module方法的实现在/ext/session/session.c 1021行开始。如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
</pre></td><td class="code"><pre class="c" style="font-family:monospace;"><span style="color: #339933;">#define MAX_MODULES 10</span>
<span style="color: #339933;">#define PREDEFINED_MODULES 2</span>
&nbsp;
<span style="color: #993333;">static</span> ps_module <span style="color: #339933;">*</span>ps_modules<span style="color: #009900;">&#91;</span>MAX_MODULES <span style="color: #339933;">+</span> <span style="color: #0000dd;">1</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span>
	ps_files_ptr<span style="color: #339933;">,</span>
	ps_user_ptr
<span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
PHPAPI <span style="color: #993333;">int</span> php_session_register_module<span style="color: #009900;">&#40;</span>ps_module <span style="color: #339933;">*</span>ptr<span style="color: #009900;">&#41;</span> <span style="color: #808080; font-style: italic;">/* {{{ */</span>
<span style="color: #009900;">&#123;</span>
	<span style="color: #993333;">int</span> ret <span style="color: #339933;">=</span> <span style="color: #339933;">-</span><span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
	<span style="color: #993333;">int</span> i<span style="color: #339933;">;</span>
&nbsp;
	<span style="color: #b1b100;">for</span> <span style="color: #009900;">&#40;</span>i <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span> i <span style="color: #339933;">&lt;</span> MAX_MODULES<span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
		<span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span>ps_modules<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
			ps_modules<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> ptr<span style="color: #339933;">;</span>
			ret <span style="color: #339933;">=</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
			<span style="color: #000000; font-weight: bold;">break</span><span style="color: #339933;">;</span>
		<span style="color: #009900;">&#125;</span>
	<span style="color: #009900;">&#125;</span>
	<span style="color: #b1b100;">return</span> ret<span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span>
<span style="color: #808080; font-style: italic;">/* }}} */</span></pre></td></tr></table></div>

<p>从上面的代码我们可能看到：PHP中的存储方式只能有10种，如果需要更多的方式，则需要修改MAX_MODULES的值，重新编译PHP。<br />
存储方式的添加顺序与对应的扩展模块加载顺序有关。</p>
<p><strong>【关于session_set_save_handler】</strong><br />
此函数用于将session的存储方式设置为用户自定义的函数，此函数的各参数对应ps_module结构的各个部分，并且会是新session.save_handler配置为user<br />
在将用户定义的函数设置为session的存储方式时会提前判断这些用户函数是否可用。如果存在一个函数不可用，则警告并退出，表示设置失败。</p>
<p><strong>【关于各方法的调用】</strong><br />
对于各存储方式定义的函数以类似于PS(mod)->s_open的方式调用。<br />
对于用户自定义的方法，通过PS(mod_user_names)保存函数名，在mod_user.c中以类似于retval = ps_call_handler(PSF(open), 2, args TSRMLS_CC);的方式调用。其中#define PSF(a) PS(mod_user_names).name.ps_##a</p>
<p>到此处理我们可以回答之前的问题。<br />
1、save_handler的设置是如何实现的？<br />
<strong>在php.ini中设置session.save_handler，在请求初始化时（PHP_RINIT_FUNCTION）查找对应的ps_module，将其赋值到全局变量PS(mod)，后续都是通过PS(mod)来处理session的存储操作，如果用户使用了session_set_save_handler方法设置存储方式，则此时的存储方式为user,通过user的模块方法中转调用用户指定的方法</strong><br />
2、在内部存在怎样的结构存储这些处理方法？<br />
<strong>此问题可以参见上面关于结构的说明，ps_module </strong><br />
3、不同的存储方式之间是如何替换的？<br />
<strong>可以在php.ini中设置session.save_handler修改，或者使用session_set_save_handler使用用户自定义的函数</strong><br />
4、如果以一个扩展的方法添加，则需要实现哪些方法？<br />
<strong>需要实现PS_OPEN_FUNC、PS_CLOSE_FUNC、PS_READ_FUNC、PS_WRITE_FUNC、PS_DESTROY_FUNC、PS_GC_FUNC、PS_CREATE_SID_FUNC等宏，这些分别对应ps_module的各个方法</strong><br />
5、这些方法在哪里添加到系统中的？<br />
<strong>通过php_session_register_module函数在模块初始化时加载</strong></p>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2010/12/php-source-code-35-session-save-handlers/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
