2019/10/20 (增補內容)
2024/09/27 (新增內容)
2024/10/17 (微調內容)
當我們登入系統之後,系統記得我們是誰,主要就是靠session。sessions所儲存的就是個人化的資料內容,例如,帳號。session所儲存的內容會一直保留到瀏覽器關閉為止。session的內容是個人化的,不同人登入會使用不同的session。
首先,要使用session,在該頁就要加上:
session_start();
設定session的內容,就利用$_SESSION陣列,$_SESSION是個associative array,可以利用key去新增、取得、修改資料:
$_SESSION["name"] = "Ben";
利用$_SESSION陣列取得session的內容:
echo $_SESSION["name"];
使用session的另一個好處是同個伺服器上其他頁面,都可以取得session的資料。
我們來試試看,改寫原本的登入頁面,當登入成功之後,就利用$_SESSION["account"]記住帳號。
修改一下login_process.php,加上session。
login_process.php
<?php
if ($_POST["account"] == "root" && $_POST["password"] == "password" ) {
session_start();
$_SESSION["account"] = $_POST["account"];
header("Location: success.php");
}
else {
header("Location: login.php?msg=error");
}
?>
在success.php裡,就能取得剛剛儲存在session裡的變數。
success.php
<?php
session_start();
echo "welcome, ".$_SESSION["account"]."!<p>";
echo "<a href='login.php'>login.php</a><p>";
echo "<a href='success2.php'>success2.php</a>";
?>
如果我們希望如果沒有透過登入就無法進入這個頁面,那就要多一些檢查
success.php
<?php
session_start();
if (isset($_SESSION["account"])){
echo "welcome, ".$_SESSION["account"]."!<p>";
echo "<a href='login.php'>login.php</a><p>";
echo "<a href='success2.php'>success2.php</a>";
}
else {
header("Location: login.php");
}
?>
試試看其他頁面是不是也可以取得session的內容。
success2.php
<?php
session_start();
if (isset($_SESSION["account"])){
echo "welcome, ".$_SESSION["account"]."!<p>";
echo "<a href='login.php'>login.php</a><p>";
echo "<a href='success.php'>success.php</a>";
}
else {
header("Location: login.php");
}
?>
萬一使用者把頁面關了,下個使用者可以直接打網址也就可以不用登入就直接進入系統,所以,一定還要加個登出的頁面:
logout.php
<?php
session_start();
unset($_SESSION["account"]);
header("Location: login.php");
?>
也可以使用session_unset()或session_destroy() (詳參: 基於session_unset與session_destroy的區別詳解)
logout.php
<?php
session_start();
session_destroy();
header("Location: login.php");
?>
修改一下success.php
<?php
session_start();
if ($_SESSION["account"]){
echo "welcome!".$_SESSION["account"]."<p>";
echo "<a href='logout.php'>Logout</a><p>";
echo "<a href='login.php'>login.php</a>";
echo "<a href='success2.php'>success2.php</a>";
}
else {
header("Location: login.php");
}
?>
session是屬於個人的,可以試著去連別人的程式,就會發現,不同人執行同一個程式,互相是不會干擾的。
該如何找到我電腦的IP位址 (Windows XP, Vista, 7, 8,10, Mac)?
For Windows 10/11
方式1
步驟1:按Windows鍵+R,然後會出現執行框,輸入控制台(control panel)並按Enter鍵。
步驟2:點選網路和網際網路(若沒找到可跳過此項)>網路和共用中心>變更介面卡設定。
步驟3:右鍵點選您的區域連線(乙太網路)圖示狀態,選取詳細資料您將會看到電腦的IP位址。
注意,如果看到的是192開頭的IP,這樣的IP是私密IP,私密IP只能在同一個區域網路中有效 (如:家裡)
方式2
步驟1:右鍵點選螢幕右下角電腦圖示的開啟網路和網際網路設定。
步驟2:點選乙太網路,並選取畫面右側的變更介面卡選項。
步驟3:右鍵點選您的區域連線(乙太網路)圖示狀態,選取詳細資料您將會看到電腦的IP位址。
方式3
打開終端機,輸入「ipconfig」
我們原本的系統都是獨立的網頁,可是,一般的網頁會有個導覽列,上面會有登入的按鈕,登入後會有登入者的名字(或帳號),我們要怎麼開發這樣的功能?
首先,我們先來利用bootstrap來開發介面,先產生一個導覽列
header.php
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
<div class="container-fluid">
<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Link 1</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link 2</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link 3</a>
</li>
</ul>
</div>
</nav>
接下來,導覽列要如何套用到每一頁? 最簡單的就是複製貼上,但是,如果以後修改導覽列,就要記得到每一頁修改,這不是個好方法。
除了最笨的複製貼上之外,可以利用php內建的include/require語法
使用的方式有兩種,最直覺的方式是每個檔案都include/require導覽列,這種做法比較單純,但是,未來如果還要加上權限管控,就會變得很複雜。另一種方法比較特別,是把導覽列放在index.php,然後,把其他頁面include/require到index.php裡面。多數的框架會採用的是類似第二種做法。
我們來看看第一種寫法,先把每一頁會重複的部分獨立成header.php、footer.php:
header.php
<html>
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
<div class="container-fluid">
<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="status.php">Status</a>
</li>
<li class="nav-item">
<a class="nav-link" href="conference.php">Conference</a>
</li>
<li class="nav-item">
<a class="nav-link" href="fee.php">Fee</a>
</li>
</ul>
</div>
</nav>
footer.php
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
在所有頁面就可利用require_once來引用
<?php require_once "header.php"?>
新的login.php
<?php
$msg = $_GET["msg"]??"";
?>
<?php require_once "header.php"?>
<div class="container">
<form action="login_process.php" method="post">
<input placeholder="帳號" class="form-control" type="text" name="account"><br>
<input placeholder="密碼" class="form-control" type="password" name="password"><br>
<input class="btn btn-primary" type="submit" value="登入">
<?=$msg?>
</form>
</div>
<?php require_once "footer.php"?>
新的index.php
<?php require_once "header.php";?>
<h1>首頁</h1>
<?php require_once "footer.php";?>
如果我們希望如果還沒登入就要打開登入頁面,我們可以跳轉頁面,也可以直接require頁面:
<?php
require_once "header.php";
?>
<h1>首頁</h1>
<?php
session_start();
if (!$_SESSION){
require_once "login.php";
}
?>
<?php
require_once "footer.php";
?>
接下來,我們來看一下status怎麼處理,首先,因為純粹的html無法使用include,所以,status.html及statu.php必須合併,也拿掉重複的部分:
status.php
<?php require_once "header.php"?>
if ($_POST){
echo $_POST["name"],"<br/>";
echo "Status:<br/>";
//new syntax in php 7
$statuslist = $_POST["status"]?? ["N/A"];
foreach( $statuslist as $status ) {
echo "$status <br/>";
}
$dinner = $_POST["dinner"]?? "";
echo "$dinner <br/>";
}
?>
<form action="status.php" method="post">
name:<input type="text" name="name" /><br/>
<input type="checkbox" name="status[]" value="faculty" checked="checked" /> Faculty
<input type="checkbox" name="status[]" value="student" /> Student<br/>
<input type="checkbox" name="dinner" value="dinner" checked="checked" /> Dinner needed
<input type="submit" value="Submit" />
</form>
<?php require_once "footer.php"?>
如果我們希望未登入就要登入?
<?php require_once "header.php"?>
<?php
session_start();
if (!$_SESSION["account"]){
header("Location: index.php");
}
if ($_POST){
echo $_POST["name"],"<br/>";
echo "Status:<br/>";
//new syntax in php 7
$statuslist = $_POST["status"]?? ["N/A"];
foreach( $statuslist as $status ) {
echo "$status <br/>";
}
$dinner = $_POST["dinner"]?? "";
echo "$dinner <br/>";
}
?>
<form action="status.php" method="post">
name:<input type="text" name="name" /><br/>
<input type="checkbox" name="status[]" value="faculty" checked="checked" /> Faculty
<input type="checkbox" name="status[]" value="student" /> Student<br/>
<input type="checkbox" name="dinner" value="dinner" checked="checked" /> Dinner needed
<input type="submit" value="Submit" />
</form>
<?php require_once "footer.php"?>
怎麼在status.php裡利用session取得登入帳號,這樣就不用再輸入一次姓名
接下來,請參考status的作法,把我們這幾週寫的內容串連起來 ,記得要把重複的html內容去掉
conference
一樣,取得登入帳號,不必再輸入一次姓名
fee
如果把登入的程式換成上週作業的login_process.php,也就是設定三組帳號、密碼,哪些地方不同? 要修改哪裡?
加入登出功能
請確保沒有登入成功是無法進入下一頁,也加一個登出的功能。
conference.php (未完成)
<?php
if ($_POST){
$program_price = array(0, 150, 100, 60);
$name = $_POST["name"]??"N/A";
//new syntax in php 7
$programlist = $_POST["program"]?? [0];
$price = 0;
foreach( $programlist as $program ) {
$price += $program_price[$program];
}
echo "$name ,您要繳交 $price 元 <br/>";
}
else {
header("Location: conference.html");
}
?>
<form action="conference.php" method="post">
<div class="row">
<div class="col-6">
<div class="form-floating mb-3">
<input type="text" class="form-control" id="_name" name="name" placeholder="您的姓名" required>
<label for="_name">Name</label>
</div>
</div>
</div>
<div class="row">
<div class="col">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="1" name="program[]" id="program_1" checked="checked">
<label class="form-check-label" for="program_1">
上午場 ($150)
</label>
</div>
</div>
<div class="col">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="2" name="program[]" id="program_2">
<label class="form-check-label" for="program_2">
下午場 ($100)
</label>
</div>
</div>
<div class="col">
<div class="form-check">
<input class="form-check-input" type="checkbox" value="3" name="program[]" id="program_3" checked="checked">
<label class="form-check-label" for="program_3">
午餐 ($60)
</label>
</div>
</div>
</div>
<input class="btn btn-primary" type="submit" value="Submit" />
</form>
fee.php (未完成)
<?php
$membershipFee=2000;
$program_price = array(
array(300, 150, 5500),//非會員
array(0, 0, 3000) //會員
);
$price = 0;
//檢查是否取得POST內容
if ($_POST){ //如果POST有內容,進行以下的登入檢查
$membership = $_POST["membershipFee"]??1;
$programs = $_POST["program"]??[];
//計算費用
$price += $membership*$membershipFee;
foreach( $programs as $program ) {
$price += $program_price[$membership][$program];
}
}
?>
<form action="fee.php" method="post">
<div>
會費:
<input type="radio" name="membershipFee" value=1 /> 繳交
<input type="radio" name="membershipFee" value=0 /> 不繳交
</div>
<div>
活動:
<input type="checkbox" name="program[]" value=0 /> 一日資管營
<input type="checkbox" name="program[]" value=1 /> 迎新茶會
<input type="checkbox" name="program[]" value=2 /> 迎新宿營
</div>
<input type="submit" value="確定" />
</form>
費用:<?=$price?>
<a href="fee.html"><button>重新計算</button></a>
在php裡,不同的php頁面之間的變數是無法共用的,所以,必須要靠$_GET、$_POST或$_SESSION來傳遞資料。
$_GET跟$_POST主要是對應HTTP GET及HTTP POST的變數。
HTTP POST一般而言是透過HTML form傳送資料,HTTP GET通常利用url query string傳送資料。
http://localhost/welcome_get.php?name=ben&email=benwu@im.fju.edu.tw
在welcome_get.php裡,利用$_GET取得資料。
<html>
<body>
Welcome <?php echo $_GET["name"]; ?><br>
Your email address is: <?php echo $_GET["email"]; ?>
</body>
</html>
當我們使用html form的時候,當method設定為get,或沒有寫method時,就是將form的內容透過HTTP GET送到welcome_get.php,雖然一般不會這麼用,但萬一忘了寫method時,就會被預設為GET了。
welcome_get.html
<html>
<body>
<form action="welcome_get.php" method="get">
Name: <input type="text" name="name"><br>
E-mail: <input type="text" name="email"><br>
<input type="submit">
</form>
</body>
</html>
或者,沒有指定method。
<html>
<body>
<form action="welcome_get.php">
Name: <input type="text" name="name"><br>
E-mail: <input type="text" name="email"><br>
<input type="submit">
</form>
</body>
</html>
在welcome_get.php裡,利用$_GET取得資料。
<html>
<body>
Welcome <?php echo $_GET["name"]; ?><br>
Your email address is: <?php echo $_GET["email"]; ?>
</body>
</html>
如果是使用post
welcome_post.html
<html>
<body>
<form action="welcome_post.php" method="post">
Name: <input type="text" name="name"><br>
E-mail: <input type="text" name="email"><br>
<input type="submit">
</form>
</body>
</html>
在welcome_post.php裡,利用$_POST取得資料。
<html>
<body>
Welcome <?php echo $_POST["name"]; ?><br>
Your email address is: <?php echo $_POST["email"]; ?>
</body>
</html>
$_SESSION的運作原理跟HTTP GET及HTTP POST是完全不同的,HTTP GET及HTTP POST是從資料把browser端送到server端,但是,$_SESSION則一直是儲存在server端,另一個差別是,$_GET及$_POST只在接收頁有效,到下一頁就無效了。但是$_SESSION只要被設定了,除非是時間到了或者是被清除了,否則就一直可以取得 (詳參: PHP 7 Sessions)。
設定$_SESSION變數
session_start();
$_SESSION["account"] = $_POST["account"];
取得$_SESSION變數
session_start();
$account = $_SESSION["account"];
清除特定session內容:
session_start();
unset($_SESSION["account"]);
清除session內所有的內容:
// remove all session variables
session_unset();
刪除整個session:
// destroy the session
session_destroy();
前面提到第二種做法,第二種做法比較複雜,我們讓index.php扮演router的角色,簡單的說,由index.php控制所有頁面的存取,因為這樣,我們也可以把所有頁面共有的內容放在index.php。
首先,我們可以看到login.php裡跟其他頁面裡有很多內容其實都一直重複,我們可以整理一下,把這些每頁都會重複的內容放到headerphp及footer.php,這樣做還有另一個好處,如果以後bootstrap更版,或者改用其他使用的方式,只要改header.php及footer.php就好了。
login.php
<?php
$msg = $_GET["msg"]??"";
?>
<html>
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<div class="container">
<form action="login_process.php" method="post">
<input placeholder="帳號" class="form-control" type="text" name="account">
<input placeholder="密碼" class="form-control" type="password" name="password">
<input class="btn btn-primary" type="submit" value="登入">
<?=$msg?>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
index.php的內容就會是:
<?php
require_once "header.php";
require_once "login.php";
require_once "footer.php";
?>
header.php
<html>
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
<div class="container-fluid">
<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="#">Link 1</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link 2</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link 3</a>
</li>
</ul>
</div>
</nav>
footer.php
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
login.php的內容就只需要:
<?php
$msg = $_GET["msg"]??"";
?>
<div class="container">
<form action="login_process.php" method="post">
<input placeholder="帳號" class="form-control" type="text" name="account">
<input placeholder="密碼" class="form-control" type="password" name="password">
<input class="btn btn-primary" type="submit" value="登入">
<?=$msg?>
</form>
</div>
那怎麼處理其他頁面呢? 我們就可以利用query string及$_GET["page"]來告訴index.php,我們要include哪一頁...
<?php
$page = $_GET["page"]??"index";
require_once "header.php";
require_once "$page.php";
require_once "footer.php";
?>
試試看:
http://localhost/web/index.php?page=login
接下來,假設我們要強迫使用者登入才能使用其他的頁面,要怎麼處理?
我們以Form with PHP裡「Input / Checkbox」用到的status.html為例,首先,我們會發現index.php只會把頁面轉到.php的檔案,所以,我們先把status.html及status.php合併。
status.php
<?php
if ($_POST){
echo $_POST["name"],"<br/>";
echo "Status:<br/>";
//new syntax in php 7
$statuslist = $_POST["status"]?? ["N/A"];
foreach( $statuslist as $status ) {
echo "$status <br/>";
}
$dinner = $_POST["dinner"]?? "";
echo "$dinner <br/>";
}
?>
<form action="index.php?page=status" method="post">
name:<input type="text" name="name" /><br/>
<input type="checkbox" name="status[]" value="faculty" checked="checked" /> Faculty
<input type="checkbox" name="status[]" value="student" /> Student<br/>
<input type="checkbox" name="dinner" value="dinner" checked="checked" /> Dinner needed
<input type="submit" value="Submit" />
</form>
就可以利用:
http://localhost/web/index.php?page=status
開啟status.php了
那怎麼知道已經登入了? 在query string加個status變數,如何?
login_process.php
<?php
//檢查是否取得POST內容
$account = $_POST['account'] ?? ["N/A"];
$password = $_POST['password'] ?? ["N/A"];
if ($account ="root" && $password == "password" ) {
echo "登入成功";
//header("Location: success.php");
header("Location: index.php?page=status&status=logged_in");
}
else {
echo "登入失敗";
header("Location: index.php?page=login&msg=帳密錯誤");
}
?>
index.php改成:
<?php
require_once "header.php";
$page = $_GET["page"]??"index";
$status = $_GET["status"]??"N/A";
if ($status =='logged_in'){
require_once "$page.php";
}
else {
require_once "login.php";
}
require_once "footer.php";
?>
這樣改了之後,除非透過login.php登入,否則,就無法進到status.php。
如果輸入:
http://localhost/web/status.php
還是可以進入status.php,所以,status.php就要想辦法避免被使用者直接呼叫,那怎麼辦呢?!
這時候我們就可以利用Session來解決囉~
首先,我們修改一下login_process.php
<?php
session_start();
//檢查是否取得POST內容
$account = $_POST['account'] ?? ["N/A"];
$password = $_POST['password'] ?? ["N/A"];
if ($account ="root" && $password == "password" ) {
echo "登入成功";
//header("Location: success.php");
$_SESSION["account"]=$account;
header("Location: index.php?page=status");
}
else {
echo "登入失敗";
header("Location: index.php?page=login&msg=帳密錯誤");
}
?>
index.php改成:
<?php
session_start();
require_once "header.php";
$page = $_GET["page"]??"index";
if ($_SESSION){
require_once "$page.php";
}
else {
require_once "login.php";
}
require_once "footer.php";
?>
防止未經過登入就進入這些頁面,就在這些頁面增加session的檢查。
status.php
<?php
if (!$_SESSION["account"]){
header("Location: login.php");
}
if ($_POST){
echo $_POST["name"],"<br/>";
echo "Status:<br/>";
//new syntax in php 7
$statuslist = $_POST["status"]?? ["N/A"];
foreach( $statuslist as $status ) {
echo "$status <br/>";
}
$dinner = $_POST["dinner"]?? "";
echo "$dinner <br/>";
}
?>
<form action="index.php?page=status" method="post">
name:<input type="text" name="name" /><br/>
<input type="checkbox" name="status[]" value="faculty" checked="checked" /> Faculty
<input type="checkbox" name="status[]" value="student" /> Student<br/>
<input type="checkbox" name="dinner" value="dinner" checked="checked" /> Dinner needed
<input type="submit" value="Submit" />
</form>
修改一下header.php
<html>
<head>
<meta charset="utf-8">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>
<body>
<nav class="navbar navbar-expand-sm bg-primary navbar-dark">
<div class="container-fluid">
<!-- Links -->
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="index.php?page=status">Status</a>
</li>
<li class="nav-item">
<a class="nav-link" href="index.php?page=conference">Conference</a>
</li>
<li class="nav-item">
<a class="nav-link" href="index.php?page=fee">Fee</a>
</li>
</ul>
</div>
</nav>