依赖于您认为它们应该启动会话的函数不仅是会话的代码气味,而且是整个代码设计的代码气味。
首先,一个应用程序应该只有一个入口点。进入入口点后您以后需要的其他功能是最后一个调用它的责任。
如果您有一堆要从全局范围调用的函数,则意味着您没有入口点,并且页面的每个部分都需要在加载时重新初始化应用程序的状态。因此,导致初始化一堆代码,包括每个页面上的session_start()。
我猜,这让您觉得需要将可重复的代码(包括 session_start())提取到函数中。
遗憾的是,这可能会导致如下代码:
function get_user_details() {
session_start();
$query = Db::query("SELECT id, username, email FROM users WHERE id = " . $_SESSION['id']);
return Db::fetchAll($query);
}
function get_user_news() {
session_start();
$query = Db::query("SELECT name, title FROM news WHERE userid = " . $_SESSION['id']);
return Db::fetchAll($query);
}
这对您来说可能看起来很酷,您可能有一个页面 user_details.php 调用第一个函数和分别user_news.php 调用第二个函数。他们都可能工作。
但是!您可能需要一个页面,比如说overview.php,您需要用户详细信息和用户新闻。然后你将在一个地方调用这两个函数,导致调用session_start() 两次。
这至少会导致通知错误(但可能会引发更严重的错误):Notice: A session had already been started - ignoring session_start()
而且,一旦您不是为自己编写代码,而是与团队中的其他人一起编写代码,他们就不会知道函数的行为。
一个函数要做一件事,应该从它的名字和返回类型来解释。
如果我知道一个函数返回 array 并被称为 get_user_info,我会自动假设它会查询数据库以返回带有用户信息的 assoc 数组。并且永远都不会猜到这个函数有一个很小的机会来初始化一个会话。
因此,换句话说,函数不得导致任何副作用。
为了防止这种情况,你应该回到我帖子的开头,我说应该只有一个入口点。
是一个类似 MVC 的架构,其中 index.php 控制所有内容并且路由器正在分派到要调用的页面,或者只是一个类似包含的系统,其中每个文件都包含例如app.php 顶部是session_start()。
PS:我知道该线程是关于安全功能的,但它与整个概念无关,这就是为什么这些示例与登录/注册/etc/等任何安全功能无关我猜给出的示例可能是更容易理解。
P.P.S.:在安全方面,同样的规则适用。您的函数不应该初始化会话,它应该只操作它以设置会话、cookie、所有您认为安全需要的东西,并且会话必须从入口点开始。这也很好,如果您或任何其他队友错误地尝试从外部任何地方调用该函数并且会话未启动,它将无法工作并引发错误,让您有机会了解您正在调用它来自错误的地方,并且并非所有资源都已初始化。