祝你有美好的一天。 这是针对初学者的PHP系列文章中的第一篇。 这将是不寻常的系列文章,不会出现
echo "Hello World"
,而从PHP程序员的生活中也不会有顽固的人,他们会使用少量的“作业”来巩固材料。
我将从会话开始-这是您必须使用的最重要的组件之一。 不了解他的工作原理-做生意。 因此,为了避免出现问题,我将尝试谈论所有可能的细微差别。
但是对于初学者来说,要了解为什么需要会话,我们转向起源-HTTP协议。
HTTP协议
HTTP协议是“超文本传输协议”,即“超文本传输协议”,即 其实-一个文本协议,理解起来并不困难。
最初,可以理解的是,在此协议下,仅HTML将被发送,地址和名称也将被发送,但现在它们将不再发送,并且= ^。^ =并且(•_ㅅ_••)
为了避免麻烦,让我举一个HTTP协议通信的例子。
这是请求的示例,当您请求
http://example.com
页面时,浏览器将如何发送请求:
GET / HTTP/1.1 Host: example.com Accept: text/html < >
这是一个示例响应:
HTTP/1.1 200 OK Content-Length: 1983 Content-Type: text/html; charset=utf-8 <html> <head>...</head> <body>...</body> </html>
这些是非常简化的示例,但是即使在这里,您也可以看到HTTP请求和响应包含的内容:
- 起始行 -对于请求,包含请求页面的方法和路径,对于响应-协议版本和响应代码
- 标头 -具有以冒号分隔的键值格式,每个新标题均从新行写入
- 邮件正文 -如上面的请求所述,直接HTML或数据与标头之间由两个换行符分隔,可能不存在
因此,我们有点想出该协议-它很简单,自1992年以来一直引领着它的历史,因此您不会将其命名为理想的,但是它是什么-发送请求-得到答案,仅此而已,服务器和客户端不再连接。 但是这种情况绝不是唯一可能的,我们可以获得授权,服务器必须以某种方式理解该请求来自特定用户,即 客户端和服务器必须在特定会话中进行通信。 是的,为此发明了以下机制:
- 用户授权后,服务器会生成并记住一个唯一密钥-会话标识符,并将其报告给浏览器
- 浏览器保存该密钥,并在随后的每个请求中发送
为了实现此机制,创建了Cookie(Cookie,Cookie)-计算机上的简单文本文件,按每个域的文件分类(尽管某些浏览器更高级,并且使用数据库存储SQLite),而浏览器对记录数施加了限制以及存储的数据的大小(对于大多数浏览器,这是4096字节,请参阅1997年的
RFC 2109 )
即 如果您从浏览器中窃取了cookie,您是否可以代表您进入Facebook页面? 请不要惊慌,这至少在Facebook上是无法做到的,然后,我将向您介绍一种防范这种对用户的攻击的可能方法。
现在,让我们看看我们的请求-响应如何更改,只要有授权即可:
索取 POST /login/ HTTP/1.1 Host: example.com Accept: text/html login=Username&password=Userpass
我们的方法已更改为POST,并且登录名和密码在请求正文中传输。 如果使用GET方法,则查询字符串将包含用户名和密码,从意识形态的角度来看这不是很正确,并且会以日志记录的形式(例如,以相同的
access.log
形式)产生许多副作用,并以明文形式缓存密码。
回应 HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Set-Cookie: KEY=VerySecretUniqueKey <html> <head>...</head> <body>...</body> </html>
服务器的响应将包含
Set-Cookie: KEY=VerySecretUniqueKey
标头
Set-Cookie: KEY=VerySecretUniqueKey
,这将强制浏览器将此数据保存在cookie中,并在下次访问服务器时将其发送并由服务器识别:
索取 GET / HTTP/1.1 Host: example.com Accept: text/html Cookie: KEY=VerySecretUniqueKey < >
如您所见,浏览器(请求头)和服务器(响应头)发送的头不同,尽管请求和响应(通用头)都相同
服务器通过发送的Cookie识别了我们的用户,并将进一步向他提供访问个人信息的权限。 好了,整理了会话和HTTP之后,现在您可以返回PHP及其功能了。
PHP和会话
我希望您已经在计算机上安装了PHP,因为 此外,我将给出示例,并且需要运行它们
创建PHP语言以匹配HTTP协议-即 它的主要任务是为HTTP请求提供答案,并“消亡”以释放内存和资源。 因此,会话机制在PHP的自动模式下不起作用,而在手动模式下,您需要知道要调用的内容和顺序。
在这里,您可以找到有关PHP即将死亡的主题的文章,或者以俄语提供 ,但是最好将其放在书签中,以备后用。
首先,您需要“启动”会话-为此,我们使用
session_start()函数,创建一个具有以下内容的
session.start.php文件:
<?php session_start();
使用脚本在文件夹中运行内置的PHP
Web服务器 :
php -S 127.0.0.1:8080
启动浏览器并打开开发人员工具(或
其他工具),然后转到页面
http://127.0.0.1:8080/session.start.php-您应该只看到一个空白页面,但不要急于关闭-外观服务器发送给我们的标头:

会有很多事情,我们只对服务器响应中的这一行感兴趣(干净的cookie,如果没有这样的行,则刷新页面):
Set-Cookie: PHPSESSID=dap83arr6r3b56e0q7t5i0qf91; path=/
看到此消息后,浏览器将保存一个名为“ PHPSESSID”的cookie:

PHPSESSID
默认会话名称,使用session.name指令从php.ini配置中进行调整,如有必要,可以在配置文件本身或使用session_name()函数更改名称
现在-我们刷新页面,我们看到浏览器将此Cookie发送到服务器,您可以尝试刷新页面两次,结果将是相同的:

我们所拥有的总数-理论与实践相吻合,这很好。
下一步是将任意值保存到会话中,为此,PHP中使用了超全局变量
$_SESSION
,我们将保存当前时间-为此,请调用
date()函数:
session_start(); $_SESSION['time'] = date("H:i:s"); echo $_SESSION['time'];
我们刷新页面,查看服务器时间,然后再次更新-时间已更新。 现在,确保设置的时间不会随着每次页面刷新而改变:
session_start(); if (!isset($_SESSION['time'])) { $_SESSION['time'] = date("H:i:s"); } echo $_SESSION['time'];
我们更新-时间没有改变,需要什么。 但是同时,我们还记得PHP快要死了,这意味着它将这个会话存储在某个地方,我们会找到这个地方...
一切秘密都变得清晰
默认情况下,PHP将会话存储在文件中
-session.save_handler指令负责此工作,在
session.save_path指令中查找文件保存路径,或使用
session_save_path()函数获取必要的路径。
在您的配置中,可能未指定文件的路径,然后会话文件将存储在系统的临时文件中-调用sys_get_temp_dir()函数并找出该隐藏位置。
因此,我们沿着该路径查找您的会话文件(我有这个文件
sess_dap83arr6r3b56e0q7t5i0qf91
),在文本编辑器中将其打开:
time|s:8:"16:19:51";
如您所见,这是我们的时间,这是会话存储的棘手格式,但是我们可以进行更改,更改时间,或者我们可以简单地输入任何行,为什么不这样做:
time|s:13:"\m/ (@.@) \m/";
要将此字符串转换为数组,您需要使用
session_decode()函数进行反向转换
-session_encode() -这称为序列化,仅在PHP中用于会话-这是它自己的-特殊的,尽管您可以使用标准的
PHP序列化 -在
session配置指令中写
.serialize_handler的值为
php_serialize
,您会很高兴的,并且
$_SESSION
可以不受限制地使用-现在您可以使用数字和特殊字符作为索引
|
和
!
以这个名字(在过去10多年的工作中,我从未如此:)
工作任务编写您的函数,类似于
session_decode()
函数,在这里您为会话设置了一个测试数据集(不需要解析正则表达式),并从当前会话的文件中获取用于转换的文本:
$_SESSION['integer var'] = 123; $_SESSION['float var'] = 1.23; $_SESSION['octal var'] = 0x123; $_SESSION['string var'] = "Hello world"; $_SESSION['array var'] = array('one', 'two', [1,2,3]); $object = new stdClass(); $object->foo = 'bar'; $object->arr = array('hello', 'world'); $_SESSION['object var'] = $object; $_SESSION['integer again'] = 42;
那么,我们还没有尝试过什么呢? 没错-要窃取Cookie,让我们启动另一个浏览器并向其中添加相同的Cookie。 为此,我为您编写了一个简单的javascript,将其复制到浏览器控制台中并运行它,只记得将会话标识符更改为您自己的:
javascript:(function(){document.cookie='PHPSESSID=dap83arr6r3b56e0q7t5i0qf91;path=/;';window.location.reload();})()
现在,您的两个浏览器正在查看同一会话。 上面我提到,我将讨论保护方法,以最简单的方式进行讨论-我们将会话绑定到浏览器,更确切地说,绑定到浏览器在服务器上的显示方式-我们将记住
User-Agent并每次进行检查:
session_start(); if (!isset($_SESSION['time'])) { $_SESSION['ua'] = $_SERVER['HTTP_USER_AGENT']; $_SESSION['time'] = date("H:i:s"); } if ($_SESSION['ua'] != $_SERVER['HTTP_USER_AGENT']) { die('Wrong browser'); } echo $_SESSION['time'];
伪造起来比较困难,但是仍然可以在这里添加保存并检查
$_SERVER['REMOTE_ADDR']
和
$_SERVER['HTTP_X_FORWARDED_FOR']
,这看起来或多或少就像是防止入侵者入侵我们的cookie的保护措施。
上一段中的关键字看起来像 cookie在真实项目中已经通过HTTPS协议运行了很长时间了,因此如果没有物理访问您的计算机或智能手机,没有人可以窃取它们
值得一提的是
session.cookie-httponly指令 ,由于这个
指令 ,会话JavaScript将无法访问。 另外,如果查看
setcookie()函数手册,您会注意到最后一个参数也负责HttpOnly。 请记住,此设置使您可以在几乎
所有浏览器中有效应对XSS攻击。
工作任务在代码中将检查添加到用户的IP;如果检查失败,请删除受损的会话。
一步一步
现在,我将使用以下代码作为示例(默认设置),逐步说明算法在PHP中会话的工作方式:
session_start(); $_SESSION['id'] = 42;
- 调用
session_start()
PHP通过session.name
指定的名称在cookie中搜索会话标识符-这是PHPSESSID
- 如果没有标识符,则将创建它(请参阅session_id() ),并沿着路径
session.save_path
创建一个名称为sess_{session_id()}
的空会话文件,标题将添加到服务器的响应中以设置cookie {session_name()}={session_id()}
- 如果存在标识符,则在
session.save_path
文件夹中查找会话文件:
- 我们找不到它-我们创建一个名为
sess_{$_COOKIE[session_name()]}
的空文件(标识符只能包含az
, AZ
, 0-9
范围内的字符,逗号和减号) - 查找,读取文件并将数据解压缩(请参见session_decode() )到超全局变量
$_SESSION
(文件被锁定以进行读取/写入)
- 脚本完成工作后,将使用
session_encode()
将$_SESSION
所有数据打包到路径session.save_path
名为sess_{session_id()}
(释放该锁)
工作任务在浏览器中使用名称PHPSESSID
设置任意cookie值,使其为1234567890
,刷新页面,检查是否已创建新文件sess_1234567890
有没有饼干的生活吗?
即使浏览器中禁用了cookie,PHP仍可以使用该会话,但是站点上的所有URL都将包含一个带有会话标识符的参数,是的-您是否仍需要配置它,但是是否需要它? 我不必使用它,但是如果我真的想,我只想说说在哪里挖掘:
如果您需要将会话存储在数据库中?
要将会话存储在数据库中,您将需要更改会话存储并告诉PHP如何使用它,为此,创建了
SessionHandlerInterface接口和
session_set_save_handler函数。
另外,我注意到您不需要为redis和memcache编写自己的会话处理程序-安装这些扩展时,相应的处理程序也将与它们一起使用,因此RTFM就是一切。 好吧,是的,必须在调用session_start()
;)之前指定处理程序。
工作任务实现SessionHandlerInterface
以将会话存储在MySQL中,检查其是否有效。
对于那些已经熟悉数据库的人来说,这是一个星号任务。
会话何时终止?
session.gc_maxlifetime指令负责会话的生存期。 默认情况下,此指令等于1440秒(24分钟),应理解为如果在指定时间内未访问会话,则该会话将被视为“烂”并等待其轮换删除。
另一个问题很有趣,您可以问成熟的开发人员吗?PHP何时删除过期会话的文件? 答案在官方指南中,但没有明确-请记住:
可以在调用
session_start()
函数时启动垃圾回收,启动的可能性取决于两个指令
session.gc_probability和
session.gc_divisor ,第一个充当除数,第二个充当除数,默认情况下这些值为1和100,
依此类推。 e。 收集器将启动并且会话文件将被删除的可能性约为1%。
工作任务更改session.gc_divisor
指令的值,以便垃圾收集器每次启动,请检查是否发生这种情况。
最琐碎的错误
超过一百万的错误会导致Google的结果:
无法发送会话Cookie- 标头已由发送
无法发送会话缓存限制器- 标头已发送
要获取一个文件,请创建一个具有以下内容的
session.error.php文件:
echo str_pad(' ', ini_get('output_buffering')); session_start();
在第二行中,一个奇怪的“魔术”是一个带有输出缓冲区的焦点,我将在以下文章之一中讨论它,到目前为止,我们认为这只是一个长度为4096个字符的字符串,在这种情况下,它是所有空格
首先先删除cookie,然后会收到上述错误,尽管错误文本有所不同,但本质是相同的:火车已经离开-服务器已将页面内容发送到浏览器,并且发送标头为时已晚,这在cookie中将不起作用,并且珍贵的会话标识符不会出现在cookie中。 如果遇到此错误-查找一个提前显示文本的位置,则在连接的文件之一中,该字符可能是字符
<?php
之前或之后的空格,如果是空格,则可能存在某些无法打印的线程像
BOM一样,所以要小心,这种感染不会影响到您(毕竟……荷尔蒙大笑)。
工作任务为了测试这些知识,我希望您实现自己的会话机制并使上面的代码工作:
require_once 'include/sess.php'; sess_start(); if (isset($_SESS["id"])) { echo $_SESS["id"]; } else { $_SESS["id"] = 42; }
要实施您的计划,您将需要register_shutdown_function()函数
锁扣
初学者中的另一个常见错误是尝试在会话文件被另一个脚本锁定时读取会话文件。 实际上,这并不是一个错误,这是对阻塞原理的误解:)
但是,让我们再次采取步骤:
session_start()
不仅创建/读取文件,而且将其锁定,以便在执行脚本时任何人都无法进行更改,或者从会话文件中读取不一致的数据- 该锁在脚本末尾释放
“插入”此错误非常容易,创建两个文件:
现在,如果您在浏览器中打开
lock.php
页面,然后在新选项卡中打开
start.php
,您将看到仅在执行第一个脚本后第二个页面才会打开,这会阻塞会话文件10秒钟。
有两种避免这种现象的选择-“笨拙”和“体贴”。
“斧头”使用自定义的会话处理程序在其中“忘记”以实现锁定:)
更好的选择是采用现成的选项并禁用锁(例如,memcached具有这样的选项
-memcached.sess_locking )O_o
花几个小时调试代码以查找罕见的弹出错误...
“周到”最好的方法在哪里-监视自己的会话锁定,并在不需要时将其删除:
-如果确定不需要更改会话数据,请在启动会话时使用
read_and_close
选项:
session_start([ 'read_and_close' => true ]);
因此,锁定将在读取会话数据后立即释放。
-如果您仍需要更改会话,请在更改会话后关闭会话以免录音:
session_start();
工作任务start.php
和lock.php
这两个文件的列表start.php
, lock.php
创建更多的read-close.php
和write-close.php
,您将在其中以列出的方式控制锁定。 检查锁如何工作(或不工作)。
总结
在本文中,您获得了七个任务,它们不仅涉及
会话的工作,而且还向您介绍
MySQL和
字符串函数 。 对于这种材料的吸收-您不需要单独的文章,只需提供的链接上的手册就足够了-没有人会为您阅读。 加油!
PS:如果您从该文章中学到了新知识-感谢作者-在社交网络中分享该文章;)
PPS是的,这是我
博客中的一篇跨文章,但仍然有用:)
一系列文章“ PHP初学者”: