PHP:在$ _SESSION中存储“对象”


188

我只是想知道我实际上可以将对象存储在$ _SESSION中,因此觉得很酷,因为当我跳到另一页时,我仍然有我的对象。现在,在开始使用这种方法之前,我想了解一下这是否真的是一个好主意,或者是否存在潜在的陷阱

我知道,如果我只有一个入口点,就不需要这样做,但是我还没有,所以我没有一个入口点,我真的很想保留我的对象,因为我没有那样不会失去我的状态。(现在,我还读过我应该对无状态站点进行编程,但我还不了解该概念。)

因此,在短期:是否确定存储对象的会话,是否有任何问题,它?


编辑:

临时摘要:到目前为止,我知道重新创建对象可能更好,即使它涉及再次查询数据库。

进一步的答案可能会在这方面详细说明!


13
我在2008年有多“愚蠢” :-)
markus

49
但对2014年像我们这样的愚蠢人很有用:D
Momin Al Aziz

3
您已经问过
markus的

1
你不傻!十年后,您问我要问些什么,并且给了我坚实的基础!
toddmo '18

好吧,我猜您只是在2019
麦克斯韦

Answers:


133

我知道这个话题很老,但是这个问题不断出现,并没有让我满意:

无论是将对象保存在$ _SESSION中,还是根据隐藏在隐藏表单字段中的数据将它们整块地重构,还是每次都从DB重新查询它们,都在使用状态。HTTP是无状态的(或多或少;但是请参阅GET与PUT),但是几乎每个人都想使用Web应用程序执行的操作都需要在某个地方维护状态。好像把国家推到了角落,这在某种程度上是理论上的胜利,这是错误的。状态就是状态。如果使用状态,则会失去无状态所带来的各种技术优势。除非您事先知道自己应该因此而失去睡眠,否则这并不是失去睡眠的事情。

汉克·盖伊(Hank Gay)提出的“双重打击”论点所带来的祝福使我特别困惑。OP是否正在构建分布式且负载均衡的电子商务系统?我的猜测不是。而且我将进一步假设,序列化他的$ User类或任何其他方法不会使他的服务器无法修复。我的建议:使用对您的应用程序明智的技术。$ _SESSION中的对象很好,但要遵守常识性预防措施。如果您的应用程序突然变成了可以与亚马逊媲美的流量,那么您将需要重新适应。那就是生活。


16
很好的答案,结合了我一直在阅读的想法。现代互联网需要状态。尽管某些应用程序不需要状态,并且不需要以无状态方式进行操作,但现代互联网依赖于太多基于状态的系统(又称登录!)来放弃它们!多年来,互联网大神甚至以cookie的形式结合了该基本概念,并在基本级别上以HTML本地存储的形式添加了该基本概念。避免在某些应用程序中过度使用状态可能是有道理的,但在某些情况下=!all!
RonLugge 2012年

好吧,当人类发明了火后不久我问了这个问题时,我不知道我今天所知道的很多事情……也是如此。同时,我会说可能会有一些很好的用例,但通常我会先寻找其他解决方案。仍将其标记为新接受的答案,因为另一个答案是绝对的。
markus

很少有答案使我大声笑出来。这个做了。Bravo +1
toddmo '18

114

只要在进行session_start()调用时,PHP已经遇到了类声明/定义,或者已经安装的自动加载器可以找到它,就可以了。否则,它将无法从会话存储反序列化对象。


12
谢谢!这为我修复了一个错误:D
马特·艾伦

我假设如果您具有适当的__autoload()功能,可以避免此问题。
Langel 2013年

在反序列化序列化对象时,我们必须添加类定义吗???我同意,在序列化对象时,它需要类定义,但是我是否还必须在文件中添加类定义,在该文件中必须对序列化的对象进行反序列化?
Rajesh Paul

35

HTTP是无状态协议,这是有原因的。会话将状态焊接到HTTP。根据经验,请避免使用会话状态。

更新:在HTTP级别上没有会话的概念。服务器通过为客户端提供唯一的ID并告诉客户端在每次请求时重新提交它来提供此功能。然后,服务器使用该ID作为大型Session对象哈希表的密钥。每当服务器收到请求时,它都会根据客户端随请求提交的ID从其会话对象的哈希表中查找会话信息。所有这些额外的工作对可伸缩性是双重打击(这是HTTP无状态的一个重要原因)。

  • Whammy One:它减少了单个服务器可以完成的工作。
  • 异想天开二:这使得扩展变得更加困难,因为现在您不能仅将请求路由到任何旧服务器-它们没有相同的会话。您可以将具有给定会话ID的所有请求固定到同一服务器。这并不容易,而且是单点故障(不是针对整个系统,而是针对大量用户)。或者,您可以在群集中的所有服务器之间共享会话存储,但是现在您有了更多的复杂性:网络连接的内存,独立的会话服务器等。

考虑到所有这些,您在会话中输入的信息越多,对性能的影响就越大(正如Vinko指出的那样)。就像Vinko指出的那样,如果您的对象不可序列化,则该会话将发生异常。因此,根据经验,避免在会话中添加超出绝对必要的内容。

@Vinko通常,您可以通过将要跟踪的数据嵌入到您发送回的响应中,然后让客户端重新提交它来解决服务器存储状态的问题,例如,将数据向下发送到隐藏的输入中。如果你真的需要服务器端状态跟踪,则可能应该在您的后备数据存储区中。

(Vinko补充:PHP可以使用数据库来存储会话信息,让客户端每次重新提交数据可能会解决潜在的可伸缩性问题,但是由于客户端可以控制所有事务,因此您必须注意一大堆安全性问题您的国家)


1
在HTTP级别上没有会话的概念。服务器通过为客户端提供唯一的ID并告诉客户端在每次请求时重新提交它来提供此功能。然后,服务器使用该ID作为大型Session对象哈希表的密钥。待续…
Hank Gay

1
每当服务器收到请求时,它都会根据客户端随请求提交的ID从其会话对象的哈希表中查找会话信息。所有这些额外的工作对可伸缩性是双重打击(这是HTTP无状态的重要原因)。待续…
Hank Gay

1
我想知道如何在不进行焊接状态的情况下通过HTTP实现复杂的应用程序
Vinko Vrsalovic

3
请修改您的答案以包含所有这些评论。对于Wiki而言,它更易于阅读且更好,而且无论如何,如果所有重要的内容都在注释中,我都无法选择您的答案作为接受的答案。谢谢!
markus

6
我希望我能对此再投票一下。知道你的时间。存储器参考花费100纳秒或0.0001毫秒。因此,对存储在主内存中的哈希表进行查找实际上不花时间。不O(1)告诉你什么?@whammy二:只是不要将所有请求随机路由到随机服务器?进行轮询,并保持从同一用户路由到同一服务器。哇,超级明显。您必须返回书本,以及所有30多种投票
Toskan

19
  • 无法序列化的对象(或包含无法序列化的成员)将不会像您期望的那样从$ _SESSION中退出
  • 巨大的会话给服务器增加了负担(每次对状态信息进行序列化和反序列化都很昂贵)

除此之外,我没有发现任何问题。


9

以我的经验,对于比具有某些属性的StdClass更复杂的事情,通常不值得这样做。给定会话存储的标识符,反序列化的成本始终比从数据库重新创建的成本高。看起来很酷,但是(像往常一样),概要分析是关键。


关于在每个请求中查询5x2数据表与将结果缓存在会话中并使用它之间的性能有何评论?
musicliftsme 2015年

6

我建议不要使用状态,除非您绝对需要它。如果可以在不使用会话的情况下重建对象,请执行此操作。在Web应用程序中拥有状态会使应用程序的构建变得更加复杂,对于每个请求,您都必须查看用户处于何种状态。网络应用程序)。最后,我建议保持会话对象尽可能小,因为这会影响序列化和反序列化大对象的性能。


因此,重新构建对象(包括再次执行所有数据库查询)是否更好?因为这样做的想法之一是,我不必再次向数据库查询相同的内容。
markus

3
如果对您来说很重要,它将不会再次查询数据库,请使用缓存,而不是将其存储在会话中。但是在进行诸如构建缓存之类的操作之前,请检查它是否确实对性能造成了影响。
约翰尼,

谢谢,我实际上认为不是。我应该再次查询。
markus

4

您必须记住,资源类型(例如数据库连接或文件指针)在页面加载之间不会持久存在,并且您需要无形地重新创建它们。

还请考虑会话的大小,具体取决于会话的存储方式,大小限制或延迟问题。


0

在升级软件库时,我也会提到-我们升级了软件,而旧版本中的对象与V1软件的类名在会话中,新软件在尝试构建会话中的对象时崩溃-作为V2软件不再使用这些相同的类,因此找不到它们。我们必须放入一些修复代码来检测会话对象,如果找到会话则将其删除,然后重新加载页面。最初报告此错误时,您最初会想到的最大麻烦是(非常熟悉,“很好,它对我有用” :),因为它仅影响最近使用新旧系统的用户。我们确实在启动之前就找到了它,因为我们所有的用户肯定都在其会话中使用了旧的会话变量,并且可能会导致所有人崩溃,

无论如何,正如您在修正案中建议的那样,我也认为最好重新创建该对象。因此,也许只存储id,然后在每次请求时从数据库中提取对象是更好/更安全的方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.