我正在建立一个有3种类型用户的网站:
-those who are registered and are subscribed for current month
-those that are registered, but are not subscribed for current month
-users that are not registered (you cant be subscribed if you are not regitered)
我已经创建了识别这3种用户并且行为正确的代码。我的问题是,这是要走的路吗?我以前从未做过类似的事情。或者我应该重新编程我的方法吗?
// login.php中
//connect to database and see if a user and password combination exists. Store $exists=0 if not, and $exists=1 if it exists.
session_start();
$conn = new mysqli($hn,$un,$pw,$db);
if ($conn->connect_error){
die($conn->connect_error);
}
$query = "SELECT COUNT(1) as 'exists',expiration_date FROM table WHERE email = ? AND password = ?;";
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $email, $password);
$email = $_POST["email"];
$password = hash("hashingalgorithm", "salt".$_POST["password"]."salthere");
$stmt->execute();
/* Get the result */
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$row = $result->fetch_assoc();
$exists = $row["exists"];
$expiration_date = $row["expiration_date"];
/* free results */
$stmt->free_result();
/* close statement */
$stmt->close();
$conn->close();
date_default_timezone_set('Europe/Berlin');
if ($exists==0){
echo "Wrong email or password";
$_SESSION['loginerror'] = 2;
header('Location: https://www.homepage.com/login');
}else if ($exists){
if (strtotime($expiration_date) < (strtotime("now"))){//logged in, but not subscribed
session_destroy();
session_start();
$_SESSION["authenticated"] = true;
header('Location: https://www.homepage.com');
}else{//logged in and ready to go
$_SESSION["authenticated"] = true;
$_SESSION["email"] = $email;
header('Location: https://www.homepage.com');
}
}else{
echo "An error with has occured.";
}
然后在我网站的每个页面上,我都使用此代码,以查看哪些用户访问了我
session_start();
if(isset($_SESSION["authenticated"]) && isset($_SESSION["email"])){
$email = $_SESSION["email"];
//connect to database and fetch expiration_date for a user with $email. Store it in $expiration_date
$conn = new mysqli($hn,$un,$pw,$db);
if ($conn->connect_error){
die($conn->connect_error);
}
$query = "SELECT expiration_date FROM table WHERE email = ?;";
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $email);
$email = $_SESSION["email"];
$stmt->execute();
/* Get the result */
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$row = $result->fetch_assoc();
$expiration_date = $row["expiration_date"];
/* free results */
$stmt->free_result();
/* close statement */
$stmt->close();
$conn->close();
date_default_timezone_set('Europe/Berlin');
if (strtotime($expiration_date) < (strtotime("now"))){//logged in, but not subscribed
session_destroy();
session_start();
$_SESSION["authenticated"] = true;
header('Location: https://www.homepage.com');
}else{ //html for subsribed and registered user
echo <<<_END
//html here
_END;
}
}else if(isset($_SESSION["authenticated"]) && !isset($_SESSION["email"])){
// user is logged in, but not subscribed;
echo <<<_END
//htmlhere
_END;
}else{// user is not registered nor is subscribed
echo <<<_END
//htmlhere
_END;
}
代码有效,但我担心一旦用户注册并订阅,就会在每个页面上访问数据库。我实际上是在惩罚用户注册和订阅。 是否有更好的,性能明智的方式来处理这类问题?
答案 0 :(得分:2)
此处的解决方案是检查订阅的用户(仅在他们登录您网站的第一页),即您可以使用的login.php
内,
// ...your previous code
session_start();
// initialize session variables
$_SESSION['subscribe_date'] = $_SESSION['authenticated'] = false;
if ($exists == 0){
echo "Wrong email or password";
$_SESSION['loginerror'] = 2;
header('Location: https://www.homepage.com/login');
} else if ($exists){
$_SESSION["authenticated"] = true;
if (strtotime($expiration_date) > (strtotime("now"))){ //logged in,& subscribed
$_SESSION["email"] = $email;
$_SESSION['subscribe_date'] = $expiration_date;
} else { //logged in and not subscribed, do nothin!
}
header('Location: https://www.homepage.com');
} else {
echo "An error has occured.";
}
然后,在每个其他页面上,您只需检查$_SESSION['subscribe_date']
而不是每次都触发查询
if(!empty($_SESSION["subscribe_date"]) && strtotime($_SESSION["subscribe_date"]) > (strtotime("now"))) {
// html for subscribed and registered user
} else if (!empty($_SESSION["authenticated"])) {
// html for registered user
} else {
// html for unregistered user
}
另请注意,我已删除了session_destroy();
在每个页面上调用它的一个非常糟糕的主意。如果需要,您可以取消设置会话变量。即unset($_SESSION["email"]);
您应该在用户退出时仅在结束时致电session_destroy();
。查看session_destroy()
答案 1 :(得分:1)
如果是我,我会将这些代码分离并重构为外部类/函数。但是,如果我们正在谈论您的代码的小问题,我将采取以下措施:
<强>的login.php 强>
//connect to database and see if a user and password combination exists. Store $exists=0 if not, and $exists=1 if it exists.
session_start();
$conn = new mysqli($hn,$un,$pw,$db);
if ($conn->connect_error){
die($conn->connect_error);
}
$query = "SELECT COUNT(1) as 'exists',expiration_date FROM table WHERE email = ? AND password = ?;";
$stmt = $conn->prepare($query);
$stmt->bind_param("ss", $email, $password);
$email = $_POST["email"];
$password = hash("hashingalgorithm", "salt".$_POST["password"]."salthere");
$stmt->execute();
/* Get the result */
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$row = $result->fetch_assoc();
$exists = $row["exists"];
$expiration_date = $row["expiration_date"];
/* free results */
$stmt->free_result();
/* close statement */
$stmt->close();
$conn->close();
date_default_timezone_set('Europe/Berlin');
if ($exists==0){
//removed echo, you shouldn't output anything before headers
$_SESSION['loginerror'] = 2;
header('Location: https://www.homepage.com/login');
//use exit after each header redirect
exit;
}else if ($exists){
//moved to the top to minimize code redundancy
$_SESSION["authenticated"] = true;
//it's good to have user identifier (e.g. email) accessible for all authenticated users
$_SESSION["email"] = $email;
if (strtotime($expiration_date) <= (strtotime("now"))){//logged in, but not subscribed
//unset only subscribed sessions, do not destroy all sessions
unset($_SESSION["subscribed"]);
header('Location: https://www.homepage.com');
exit;
}else{
//logged in with active subscribtion
$_SESSION["subscribed"] = true;
header('Location: https://www.homepage.com');
exit;
}
}else{
echo "An error with has occured.";
}
每页包含代码
session_start();
if(isset($_SESSION["authenticated"])){
//merged if-statement for both subscribed and unsubscribed user
$email = $_SESSION["email"];
//connect to database and fetch expiration_date for a user with $email. Store it in $expiration_date
$conn = new mysqli($hn,$un,$pw,$db);
if ($conn->connect_error){
die($conn->connect_error);
}
//check if subscribed user is still subscribed and unsubscribed user is still unsubscribed
$query = "SELECT expiration_date FROM table WHERE email = ?;";
$stmt = $conn->prepare($query);
$stmt->bind_param("s", $email);
$email = $_SESSION["email"];
$stmt->execute();
/* Get the result */
$result = $stmt->get_result();
$num_of_rows = $result->num_rows;
$row = $result->fetch_assoc();
$expiration_date = $row["expiration_date"];
/* free results */
$stmt->free_result();
/* close statement */
$stmt->close();
$conn->close();
date_default_timezone_set('Europe/Berlin');
//I have separated expiration verification and template echoing
//first - expiration verification
if (strtotime($expiration_date) <= (strtotime("now")) && isset($_SESSION["subscribed"])){
//had been subscribed a second ago, but now he's not
unset($_SESSION["subscribed"]);
header('Location: https://www.homepage.com');
exit;
}else if (strtotime($expiration_date) > (strtotime("now")) && !isset($_SESSION["subscribed"])){
//had been unsubscribed, but renewed subscription in the meantime (maybe in another browser with this session still active)
$_SESSION["subscribed"] = true;
header('Location: https://www.homepage.com');
exit;
}
//user successfully passed expiraton verification, maybe was few times redirected
//second - template echoing
if (isset($_SESSION["subscribed"])) {
// user is logged in and subscribed;
echo '';
}else{
// user is logged in, but not subscribed;
echo '';
}
}else{
// user is not registered
echo '';
}
在代码中的每次更改之前都有注释。以下是我所做的更改的简短列表:
我会做一些其他的小调整(在每个文件的顶部移动date_default_timezone_set等),但它们不是你问题的主题。
答案 2 :(得分:1)
据我了解,您已经有了一个可行的代码。你要问的是意见。您希望在检查数据库的每个页面中删除重复以进行身份验证和订阅。
在我看来,您需要更改使用会话的方式,
$_session['email'] // email address of user
$_session['auth_type'] // holds authentication type
$_session['auth_till'] // subscription expire date
然后让我们创建函数来检查订阅。此函数可以放在单独的文件中,例如:init.php。在这里,我们可以设置会话启动机制,以便在任何情况下都可以使用会话。
if(!isset($_SESSION))
session_start(); // start session if not already started
// lets define global vars to hold authentication type of visitor
define("SUBSCRIBED",1);
define("UN_SUBSCRIBED",2);
define("REGISTERED",3);
function checkSubscription():bool{
$return = false;
if(($_session['auth_type']==SUBSCRIBED)&&(strtotime($_session['auth_till']) < strtotime("now")))
$return= true;
return $return
}
并且在login.php上使用相同的技术,同时设置会话身份验证类型。
现在任何其他页面都可以使用函数来检查订阅
例如:
<?php
// file: product.php
include_once("init.php");
if(!checkSubscription()){
// subscription finished. do what you need to do. otherwise continue.
}
您的代码可以进行许多改进。但我认为这将满足您的需求。如果您需要任何其他助手,请告诉我。请访问Scape并告知我们是否有任何有用的编码。
答案 3 :(得分:1)
在每次加载页面之前,使用PHP会话变量存储有关当前用户的信息。
例如,当用户登录时,将用户ID保存到会话变量中的该用户会话:
$_SESSION['user_id'] = <userid>;
在下一页加载时,在运行数据库查询之前检查该会话变量。
if ($_SESSION['user_id']) {
// normal operations
} else {
// Make your database call
}
答案 4 :(得分:1)
一般来说,当用户第一次登录时,在$_SESSION
中设置两个状态标志:
$_SESSION['loggedIn'] = true
$_SESSION['subscribed'] = bool //true or false
我假设只有注册用户才能登录。如果用户做了某些事情来改变他或她的状态,请相应地更新$_SESSION
。
请注意,在检查值之前,请务必检查会话是否处于活动状态。另外,使用session_regenerate_id
来阻止会话固定。
真正复杂的类型可能会尝试序列化一个User
对象并将其存储在$_SESSION
中。然后,在每个页面加载时,User
对象的属性可以封送(未序列化),并在User
对象的新实例中再次生效。在那个世界中,您只需检查User
对象的属性,看看他或她是否(a)已登录并且(b)已订阅。 User
对象的状态将是您的关注点,而不仅仅是$_SESSION
超全局中的孤立值。
PHP手册:Object Serialization
答案 5 :(得分:1)
在login.php上你已从数据库中获取expiration_date
,你可以将它存储在会话变量中
$_SESSION['expiration_date'] = $row['expiration_date'];
并且在每个页面上都可以使用上面的值来检查而不是查询数据库
if (isset($_SESSION['expiration_date']) && (strtotime($_SESSION['expiration_date']) < (strtotime("now") ))
答案 6 :(得分:1)
PHP会话模型允许启动会话并稍后获取有关序列化并存储在$_SESSION
数组中的会话的所有数据。
担心导致每个页面访问的数据库调用是正确的。会话数据可以使用cookie
设置,也可以在POST
或GET
方法中返回。因此,您需要定义合适的PHP会话ID retreival和会话生命周期的方法。
$_SESSION['name'] = 'value'; // Stores Session values to avoid database requests
...
session_id($sid); // sets the id of existing session
session_start(); // retreive stored session data;
您可以设置一次registered
和subscribed
这样的标记,以便以后使用,在会话生命周期或特殊操作期间没有数据库请求:
会话启动时,subscribed
和registered
标志将设置为FALSE
,直到验证通过。所以,我的意见 - 每个会话只需要调用三次数据库:用户进行身份验证,注册或订阅时。如代码所示,您可以通过POST
传输数据。将sid
隐藏字段添加到所有表单以保留会话ID。而SELECT
来自数据库所有需要的数据(当然不是密码),在会话期间与用户交互时非常有用。