一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

簡單 PHP + MySQL 數(shù)據(jù)庫動(dòng)態(tài)網(wǎng)站制作 | renfei song's blog

 閱文書苑 2014-06-10

內(nèi)容提要 [隱藏]

由于學(xué)校社團(tuán)活動(dòng)需要一個(gè)支持?jǐn)?shù)據(jù)庫和用戶提交信息的動(dòng)態(tài)網(wǎng)站,所以花了幾個(gè)晚上速成了一下 PHP 和 MySQL 的相關(guān)知識(shí),之后就寫了一個(gè)網(wǎng)站,結(jié)果表明運(yùn)行的很順利。之前感覺這種動(dòng)態(tài)網(wǎng)站很深?yuàn)W,以為會(huì)很難,可是沒想到如果只實(shí)現(xiàn)不太復(fù)雜的功能的話,其實(shí)并不很難。

我爭取用最淺顯易懂的語言來說明一下是如何實(shí)現(xiàn)的。閱讀本文需要簡單的 HTML 基礎(chǔ)知識(shí)和(任一編程語言的)編程基礎(chǔ)知識(shí)(例如變量、值、循環(huán)、語句塊的概念等)。

PHP 基礎(chǔ)

概述

PHP 是一種解釋性語言,可用于對網(wǎng)頁進(jìn)行預(yù)處理。PHP 腳本在服務(wù)器端運(yùn)行,其運(yùn)行結(jié)果是一個(gè)可用來顯示的網(wǎng)頁。盡管可以完成許多類似工作,但是 Javascript 和 PHP 的一大區(qū)別就是,Javascript 是在瀏覽器端運(yùn)行的。事實(shí)上,瀏覽器會(huì)接收 Javascript 代碼,并運(yùn)行它,所以理論上用戶是可以檢查 Javascript 代碼的。而 PHP 不會(huì)將原始代碼交給瀏覽器, 只會(huì)將其運(yùn)行的結(jié)果交給瀏覽器,所以用 PHP 處理用戶登陸、用戶權(quán)限等問題是安全可靠的。

PHP 與 HTML

實(shí)際編寫的時(shí)候,通常采用的方式是建立擴(kuò)展名為 php 的文件(網(wǎng)頁文件本質(zhì)上是文本文件)。編寫 php 代碼和編寫 html 代碼并沒有多少區(qū)別,而最方便的地方在于,在一個(gè) php 文件中,兩種代碼是可以混編的。

規(guī)則:php 代碼需要包含在 <?php ... ?> 標(biāo)簽中,就像這樣:

<?php

    // code goes here

?>

提示:這是一個(gè) php 和 html 混編的較為生動(dòng)的例子。

<?php

    if ($var == "true") {

?>

<html id="ie6">

<?php

    } else {

?>

<html id="ie8">

<?php

    }

?>

這里的意思是,如果 php 中的變量 $var 的值為 true,則放置一個(gè)標(biāo)簽,否則放置另一個(gè)標(biāo)簽。

關(guān)于 PHP 中的操作符

PHP 采用的操作符和 C/C++ 是類似的,例如用 = 表示賦值,== 表示相等性比較,以及 <> (小于、大于)比較符、! 取反、&&邏輯與、||邏輯或等。當(dāng)然,也支持 +-*/ 等數(shù)學(xué)表達(dá)式的運(yùn)算。

關(guān)于 PHP 中的變量

PHP 中變量的命名一律以符號(hào) $ 開頭,可以使用下劃線,例如 $is_logged_in 就是一個(gè)表意清晰的變量名。和大多數(shù)編程語言不同,PHP 中變量沒有類型的概念,而且不用聲明就可以直接使用。雖然很爽,但是變量多了的時(shí)候也容易混亂,這一點(diǎn)需要特別注意。

關(guān)于 PHP 中的語句

這一點(diǎn) PHP 和許多其他常見的編程語言很類似,也可以用 if...else 選擇語句(之前已經(jīng)見過了),PHP 還包括 while 循環(huán)、foreach 循環(huán)等,以后遇到了會(huì)詳細(xì)介紹。

MySQL 基礎(chǔ)

使用 MySQL 數(shù)據(jù)庫是存儲(chǔ)數(shù)據(jù)的一種方法,MySQL 需要和 PHP 配合來完成對數(shù)據(jù)庫的查詢(這里術(shù)語“查詢”包括寫入、更新、讀取等)操作。利用 MySQL,你可以創(chuàng)建許多數(shù)據(jù)庫(database),每個(gè)數(shù)據(jù)庫可以包含多個(gè)表(table),而每個(gè)表包含若干字段。為了高效,一般會(huì)采取分類維護(hù)多個(gè)表的方式,而不是把所有數(shù)據(jù)都儲(chǔ)存在同一個(gè)表中。

MySQL 需要服務(wù)器支持。使用的第一步是建立一個(gè)數(shù)據(jù)庫,可以用相應(yīng)的圖形化工具(例如 phpMyAdmin)來建立數(shù)據(jù)庫,也可以在終端直接使用下列 SQL 語句來創(chuàng)建一個(gè)名為 database_name 的數(shù)據(jù)庫:

CREATE DATABASE database_name;

創(chuàng)建好數(shù)據(jù)庫之后,需要?jiǎng)?chuàng)建表??梢酝ㄟ^下列 SQL 語句來創(chuàng)建一個(gè)名為 table_name 的表:

USE database_name;

CREATE TABLE table_name (
  first_name varchar(30),
  last_name varchar(30),
);

第一句說明在哪個(gè)數(shù)據(jù)庫中添加表,第二個(gè)說明添加的表的詳情。這里我們在表中添加了兩個(gè)字段,分別叫做 first_namelast_name,它們的類型是 varchar(30)。其中 varchar 是一種可變長字符類型,而 30 代表了最大長度。

其他常見的數(shù)據(jù)類型如下:

VARCHAR(100) --可變字符 
CHARACTER(1) --定長
INTEGER --整數(shù)
DECIMAL(10, 2) --小數(shù)(小數(shù)點(diǎn)前后的位數(shù))
TIMESTAMP --日期和時(shí)間
DATE --日期

你可能已經(jīng)看出來了,MySQL 的注釋符為 --。你可能覺得沒太大用,但是它卻是一種稍后要提到的攻擊的關(guān)鍵之處。

使 PHP 和 MySQL 協(xié)作

第一種方式

現(xiàn)在你已經(jīng)創(chuàng)建好了 SQL 數(shù)據(jù)表,并對 PHP 語言有了一個(gè)概覽。下面我們直奔主題,學(xué)習(xí)如何對數(shù)據(jù)表進(jìn)行查詢。

為了使 PHP 和 MySQL 進(jìn)行交互,需要為 PHP 提供你的數(shù)據(jù)庫用戶名、密碼以及數(shù)據(jù)庫名、數(shù)據(jù)表名,當(dāng)然,最重要的,查詢操作的 SQL 語句。我們一一來觀察是如何實(shí)現(xiàn)的。

<?php

    define('DB_HOST', 'localhost');
    define('DB_USER', 'renfei');
    define('DB_PASS', 'root');
    define('DB_NAME', 'database_name');

    $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    $query = "INSERT INTO table_name (column_name1, column_name2) VALUES ('value1', 'value2')";

    mysqli_query($dbc, $query);

?>

下面來解釋一下這一坨代碼的工作原理。

  • 首先 3~6 行為 PHP 中的 define 語句,作用很明顯,把 DB_HOST 定義為 localhost,下面的代碼中就可以使用 DB_HOST 來代替 localhost。這樣做的好處在于,如果這些內(nèi)容在代碼中出現(xiàn)多次,修改的時(shí)候非常方便。
  • 然后是一個(gè)叫做 mysqli_connect() 的函數(shù),它需要四個(gè)變量,分別是主機(jī)名、用戶名、密碼、數(shù)據(jù)庫名。這個(gè)函數(shù)執(zhí)行后的返回值傳遞給變量 $dbc,$dbc 包含了一次數(shù)據(jù)庫連接。注意,這個(gè)變量名是任意的。
  • 然后,我們把要對數(shù)據(jù)庫執(zhí)行的操作對應(yīng)的 SQL 語句以字符串的形式賦給變量 $query。這個(gè)變量名也是任意的。應(yīng)該注意到,這里的 SQL 語句是不以分號(hào)結(jié)尾的。
  • 最后,我們執(zhí)行 mysqli_query(); 函數(shù),該函數(shù)有兩個(gè)參數(shù),分別是一個(gè)數(shù)據(jù)庫連接,和一個(gè) SQL 查詢操作。執(zhí)行該函數(shù)后,相應(yīng)的查詢操作被執(zhí)行。

事實(shí)上,如果把這些代碼保存成一個(gè)網(wǎng)頁,當(dāng)用戶打開網(wǎng)頁的時(shí)候,如果各項(xiàng)參數(shù)正確,它就會(huì)完整地運(yùn)行下去。

這里的 SQL 語句的含義是向叫做 table_name 的表中插入一行,其中把 colume_name# 字段的值相應(yīng)地設(shè)置為 value#。這里只設(shè)定了兩個(gè)字段的數(shù)值(表中還可以有其他字段;沒有顯式說明的字段則留空或者使用數(shù)據(jù)表指定的默認(rèn)值)。該語句的通用形式為:

INSERT INTO table_name (column_name1, column_name2, ...) VALUES ('value1', 'value2', ...)

如果你要做的僅僅是執(zhí)行一個(gè) SQL 語句,那么使用這種模式就可以完成。提醒一下,$dbc 變量往往是重復(fù)使用的。

另一個(gè)常用的 SQL 語句就是修改某一行。它的形式為:

UPDATE table_name SET
column_name1='preferred_value1',
column_name2='preferred_value2',
...,
WHERE id = '$id'

當(dāng)然,這個(gè)語句應(yīng)該是寫到一行的,不過為了清晰我分開來寫。它的含義是,修改名為 table_name 的表中字段 id 的值是變量 $id 的值的所有行,把 column_name1 字段的值設(shè)為 preferred_value1,把 column_name2 字段的值設(shè)為 preferred_value2。這里我們還看到,值既可以用常量表示,也可以用變量表示。

注意:會(huì)修改所有符合 WHERE 子句限定的條件的行(如果省略 WHERE 子句,就會(huì)修改所有行)。WHERE 子句可以設(shè)定多個(gè)條件,例如寫成:

WHERE id = '$id' AND is_male = 'true'
WHERE id = '$id' OR pid = '$id'

都是合法的(如果你想問 ANDOR 為什么不是符號(hào) &&|| 的話,我想提示你,不要把 PHP 語言和 SQL 語言搞混了。這是 SQL 語言,而我只說過 PHP 語言和 C/C++ 有些類似)。

下面介紹其他 SQL 語句。

--更新table_name表中的數(shù)據(jù)
UPDATE

--刪除table_name表中的所有行
DELETE FROM table_name

--刪除table_name表中email字段為a@do.com的所有行
DELETE FROM table_name WHERE email = 'a@do.com'

--刪除table_name表
DROP TABLE table_name

--刪除table_name表中的score字段
ALTER TABLE table_name DROP COLUMN score

--給table_name表添加一個(gè)叫age的字段,類型為 INTEGER
ALTER TABLE table_name ADD COLUMN age INTEGER

--把table_name表中的col字段改名為loc,并設(shè)類型為 INTEGER
ALTER TABLE table_name CHANGE COLUMN col loc INTEGER

可見,第一種方式的本質(zhì)就是編寫一條 SQL 語句,然后通過 PHP 來執(zhí)行它。下面,我們來看第二種方式。

第二種方式

有時(shí),我們不滿足于讓服務(wù)器去執(zhí)行一條 SQL 語句。我們會(huì)需要從數(shù)據(jù)庫中查詢信息,然后把得到的信息儲(chǔ)存起來(其實(shí)就是儲(chǔ)存在變量中)。這樣,我們需要一些額外的工作。先看一坨代碼:

<?php

    $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    $query = "SELECT * FROM table_name WHERE problem_id='$id'";
    $result = mysqli_query($dbc, $query);
    $row = mysqli_fetch_array($result);
    $problem_title = $row['problem_title'];

?>

這里我們省略了 define 語句。

這一坨代碼和上一坨的主要區(qū)別是,我們使用了 mysqli_query() 函數(shù)的返回值,把它保存到 $result 變量中。這個(gè) $result 變量里邊保存的即為執(zhí)行 SELECT 語句的返回結(jié)果。

解釋一下 SELECT 語句,它的作用是選取 table_name 表中符合 WHERE 子句條件的所有行。上面的語句會(huì)選定每一行的所有字段(通配符說明了這一點(diǎn)),并且把這些信息全部儲(chǔ)存到變量 $result中。

然后,用變量 $row 儲(chǔ)存 mysqli_fetch_array() 函數(shù)的返回值。$row 這個(gè)變量非常神奇,$row['column_name'] 這個(gè)事兒包含的內(nèi)容正是剛才選定的行的 column_name 字段的值(事實(shí)上,$row 正是一個(gè)數(shù)組)。這里,我們把它賦給了 $problem_title 變量。

到這里你應(yīng)該問一個(gè)問題:如果滿足 WHERE 子句條件的有不止一行的話怎么辦?要解答這個(gè)問題,需要稍微細(xì)致的講解一下 $row 這個(gè)事兒。如果滿足條件的只有一行,那么使用 $row = mysqli_fetch_array($result) 自然會(huì)把這唯一的一行信息儲(chǔ)存到 $row 中。如果有很多行,那么第一次使用 $row = mysqli_fetch_array($result) 會(huì)把第一行的信息儲(chǔ)存到 $row 中,而第二次使用 $row = mysqli_fetch_array($result) 會(huì)把第二行的信息儲(chǔ)存到 $row 中。如果這時(shí)沒有下一行了,再次調(diào)用的話 $row 會(huì)儲(chǔ)存邏輯假(false0)。類似,如果符合 WHERE 子句條件的一行都沒有,那么執(zhí)行后 $row 直接存儲(chǔ)邏輯假。

最后補(bǔ)充一點(diǎn)剛才沒有提到的。如果不需要所有字段的數(shù)據(jù),可以只選擇需要的字段。方法是把原來 SQL 語句中的通配符換成字段名稱。例如:

SELECT problem_name, problem_type FROM table_name WHERE problem_id='$id'

while 循環(huán)在 PHP 中的應(yīng)用舉例

如果我們要把一個(gè)數(shù)據(jù)庫的許多行信息都展示在網(wǎng)頁中,那么需要用到 while 循環(huán)和上面的第二種方式。代碼如下:

<?php

    $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
    $query = "SELECT user_id FROM database_name ORDER BY user_id ASC";
    $result = mysqli_query($dbc, $query);

    while ($row = mysqli_fetch_array($result)) {
        $user_id = $row['user_id'];
        echo "<tr>";
        echo "<td>".$user_id."</td>";
        echo "</tr>";
    }

?>

如果有一定編程基礎(chǔ)的話上面的代碼很容易看懂。上面新出現(xiàn)了三種用法,說明如下:

  • SELECT 語句可以附加一個(gè) ORDER BY 子句,用來控制順序。例如這里是按照字段 user_id 升序排列。下面的例子會(huì)先按照 user_rank 降序排列,user_rank 相同時(shí)按照 user_id 升序排列:
SELECT user_id, user_rank FROM table_name ORDER BY user_rank DESC, user_id ASC
  • 關(guān)于 PHP 中的 echo 語句,它可以用來生成文本,類似于 C 中的 printf() 函數(shù)。這里利用它直接生成 HTML 代碼。它的用法參考例子就可以了。
  • 關(guān)于符號(hào) . 的用法,它的作用是連接字符串(和變量),往往和 echo 配合使用,用法參考示例。

從表單獲取信息

概述

這一部分我們演示如何構(gòu)建一個(gè)表單,使用戶填寫這個(gè)表單并把內(nèi)容儲(chǔ)存到數(shù)據(jù)庫。這一技術(shù)是用戶注冊系統(tǒng)和用戶互動(dòng)的基礎(chǔ)。

要實(shí)現(xiàn)這個(gè)功能,需要 HTML 和 PHP 配合完成。HTML 負(fù)責(zé)表單,而 PHP 負(fù)責(zé)獲取信息并使用 SQL 查詢儲(chǔ)存信息。首先來看 HTML 部分(就是普通的表單):

<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
    <label for="username">用戶名:</label>
    <input type="text" id="username" name="username" /><br/>
    <label for="info">信息:</label>
    <input type="text" id="info" name="info" /><br/>

    <input type="submit" value="Submit" name="submit"/>
</form>

屬于 HTML 部分的不再解釋了,說一說新鮮的。這里的 action 屬性后面的 $_SERVER['PHP_SELF'](嚴(yán)格地說,$_SERVER),是 PHP 的一個(gè)超級全局變量,內(nèi)容是當(dāng)前頁面的相對路徑,例如 signup.php。這個(gè) action 屬性的含義是指定用戶填寫的信息在哪里被處理,這里是在當(dāng)前頁面處理。一般的做法都是將負(fù)責(zé)處理這部分信息的 PHP 代碼和 HTML 代碼放在同一頁面內(nèi)。

下面來看一下相應(yīng)的 PHP 處理部分的代碼:

<?php

    $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);

    if (isset($_POST['submit'])) {
        $user = $_POST['username'];
        $info = $_POST['info'];
        $query = "INSERT INTO table_name (tb_user, tb_info) VALUES ('$user', '$info')";
        mysqli_query($dbc, $query);	
        echo "<p>提交成功</p>";
    }

    mysqli_close($dbc);

?>

首先仍然是建立數(shù)據(jù)庫連接。當(dāng)用戶點(diǎn)擊 sumbit 按鈕后,表單的內(nèi)容會(huì)被儲(chǔ)存在 PHP 中 $_POST 超級全局變量內(nèi),這個(gè)超級全局變量仍然是一個(gè)數(shù)組。isset() 函數(shù)用來檢查變量是否被設(shè)置,只有用戶點(diǎn)擊 submit 后 isset($_POST['submit']) 才返回真,所以不用擔(dān)心,首次加載表單(那時(shí)用戶還沒有填寫任何內(nèi)容)是不會(huì)執(zhí)行這部分 PHP 代碼的,只有用戶提交之后才會(huì)執(zhí)行。用戶填寫的具體內(nèi)容可以用 $_POST['name'] 來獲取。這里的 name 對應(yīng)的是 HTML 中 name 屬性的內(nèi)容。這一段程序把用戶填寫的內(nèi)容賦給變量,然后執(zhí)行插入到數(shù)據(jù)庫的操作。

這里新出現(xiàn)了一個(gè)內(nèi)容,就是 mysqli_close() 函數(shù),它的作用是關(guān)閉數(shù)據(jù)庫連接。當(dāng)我們不再需要這個(gè)連接的時(shí)候,及時(shí)關(guān)閉是一個(gè)好主意。

需要注意的是,這僅僅是最簡單的代碼,而且實(shí)際上是不完善的。如果要真正投入使用,我們需要使它更健壯一些。下面逐一討論這些內(nèi)容。

檢查用戶輸入是否合法

如果用戶根本沒有填寫表單,就直接點(diǎn)擊提交按鈕,會(huì)發(fā)生什么?在上面的實(shí)例中,PHP 依然會(huì)乖乖地把空內(nèi)容插入,而這顯然是垃圾信息,不是我們需要的。所以,需要在插入前檢查被插入的變量是否為空。例如:

<?php

    if (!empty($user) && !empty($info)) {
        // 插入操作
    }

?>

這里出現(xiàn)了 empty() 函數(shù),用于檢查內(nèi)容是否為空。注意這里使用 isset() 是無效的,因?yàn)?isset() 檢查的是是否“被設(shè)置”,而被設(shè)置為空也屬于被設(shè)置。

錯(cuò)誤提示

用戶輸入有誤時(shí),上面的改進(jìn)除了不執(zhí)行SQL查詢,并沒有多少直觀上的變化。用戶不會(huì)收到任何信息表明他們的填寫是不合適的。所以我們要在這時(shí)產(chǎn)生一些提示,引導(dǎo)用戶正確填寫表單。

<?php

    if (!empty($user) && !empty($info)) {
        // 插入操作
    } else {
        echo "請?zhí)顚懭績?nèi)容后再提交";
    }

?>

防范 SQL 注入攻擊

我們執(zhí)行的 SQL語句中包含變量,執(zhí)行的時(shí)候會(huì)直接把變量內(nèi)容替換進(jìn)去。而如果攻擊者在輸入框中輸入一些危險(xiǎn)的字符(通常包含 SQL 注釋符 --,以及其他預(yù)先精心設(shè)置的內(nèi)容),就可能導(dǎo)致該次 SQL 查詢完全被改寫成攻擊者需要的意思。為了防范這種攻擊,我們需要對可能存在的危險(xiǎn)字符進(jìn)行過濾和轉(zhuǎn)義,較為便捷的方法是使用兩個(gè)函數(shù)。改進(jìn)后的部分如下:

<?php

    $user = mysqli_real_escape_string($dbc, trim($_POST['username']));
    $info = mysqli_real_escape_string($dbc, trim($_POST['info']));

?>

粘性表單

如果用戶第一次填寫失敗,他們希望能保留已經(jīng)填寫好的內(nèi)容,只做些修改就好了。這需要使用粘性表單技術(shù)。要實(shí)現(xiàn),只需要稍稍改動(dòng) HTML 表單部分的代碼:

<label for="username">用戶名:</label>
<input type="text" id="username" name="user" value="<?php if (!empty($user)) echo $user; ?>" />

顯而易見,如果用戶填寫后因?yàn)槟承┰驔]有提交而是回到了這個(gè)表單,并且之前填寫了 user 字段的內(nèi)容,那么此時(shí) $user 變量已經(jīng)被賦值了。那么就會(huì)在 HTML 表單顯示這些內(nèi)容,避免用戶再次輸入。

構(gòu)造一個(gè)注冊頁面

雖然上面說了很多,但是僅僅滿足了我們最基本的輸入要求。許多時(shí)候我們需要更為復(fù)雜的功能。舉例來說,要寫一個(gè)注冊頁面,必須檢查用戶名是否重復(fù),還要對密碼采取某種技術(shù)加密以保證安全。

檢查用戶是否重復(fù)

基本原理就是,根據(jù)需要判重的字段(例如用戶名)去數(shù)據(jù)庫搜索。如果發(fā)現(xiàn)結(jié)果則用戶名重復(fù),如果沒有找到則允許注冊。需要一個(gè)新函數(shù) mysqli_num_rows(),返回 SELECT 語句得到的行數(shù),根據(jù)其是否等于 0 進(jìn)行判斷。

<?php  

    $dbc = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);    
    $query = "SELECT * FROM table_name WHERE user_name = '$user'";
    $data = mysqli_query($dbc, $query);
    if (mysqli_num_rows($data) == 0) {
        // 把內(nèi)容插入數(shù)據(jù)庫
        echo "注冊成功";
        mysqli_close($dbc);
        exit();
    } else {
        echo "用戶名已被占用,請重新選擇用戶名";
        $user = "";
    }

?>

$user 清空是為了配合粘性表單。

需要說明的是 exit(); 函數(shù),它會(huì)立刻終止 PHP 的運(yùn)行。因?yàn)橛脩粢呀?jīng)注冊成功,沒有必要執(zhí)行后面的任何代碼,所以使用這個(gè)函數(shù)。寫自己的程序的時(shí)候可以親自試驗(yàn)是否需要這一行、PHP 和 HTML 在 php 中的順序不同有何影響。我通常的做法是把 PHP 代碼放在前面,HTML 代碼放在后面。

對密碼進(jìn)行加密存儲(chǔ)

明文存儲(chǔ)密碼是對用戶很不負(fù)責(zé)的,不僅數(shù)據(jù)庫管理員可以看到密碼,一旦數(shù)據(jù)庫泄漏,密碼就會(huì)被公開。所以,我們應(yīng)該加密存儲(chǔ)用戶密碼。在 PHP 中,可以使用 SHA() 函數(shù)進(jìn)行加密,它是一種不可逆的加密,加密后會(huì)生成定長的一段字符串,并且是無法由這段字符串還原原密碼的。

加密的原理是,用戶輸入密碼后,利用 PHP 把 hash 過的密碼儲(chǔ)存在數(shù)據(jù)庫中。用戶登陸的時(shí)候,把用戶輸入的密碼進(jìn)行 hash 運(yùn)算,之后和數(shù)據(jù)庫中的進(jìn)行比對。

使用方法如下:

SHA('$password')

其他要素

對于注冊頁面,不要忘記確認(rèn)密碼,即讓用戶輸入兩次,比較確定相等后再執(zhí)行注冊。

識(shí)別用戶登錄:Cookie

只注冊沒有用,必須添加登錄功能。登錄功能可以使用 Cookie 來實(shí)現(xiàn)。這里假定你已經(jīng)了解 Cookie 的基礎(chǔ)知識(shí),只說如何實(shí)現(xiàn)。

設(shè)置 Cookie

<?php

    setcookie('user_type', $user_type, time() + (60 * 60 * 24 * 30));

?>

上面的代碼用來設(shè)置 Cookie,其中函數(shù)的第一個(gè)參數(shù)為 Cookie 名稱,第二個(gè)參數(shù)為數(shù)值(這里用一個(gè)變量傳遞),第三個(gè)參數(shù)為過期時(shí)間,單位秒。示例為一個(gè)月。

可以用設(shè)置多個(gè) Cookie 來存儲(chǔ)許多內(nèi)容,例如用戶 ID、用戶組(管理員還是普通用戶)等。

驗(yàn)證 Cookie

用戶登陸后,我們可以設(shè)置一個(gè) Cookie 來存儲(chǔ)登錄信息(即哪個(gè)用戶登陸的),然后通過檢查這個(gè) Cookie 來設(shè)定相應(yīng)功能。

<?php

    if (isset($_COOKIE['user_type'])) {
        $user_type = $_COOKIE['user_type'];
    }

?>

刪除 Cookie

要?jiǎng)h除 Cookie,只需要把過期時(shí)間設(shè)定在過去。

<?php

    setcookie('user_type', '', time() - 3600);

?>

不要問我為什么設(shè)定在過去一個(gè)小時(shí),設(shè)定幾個(gè)小時(shí)都沒問題。

Cookie 的安全性

設(shè)置 Cookie 有其潛在的危險(xiǎn)。由于 Cookie 是保存在用戶本地的,所以用戶完全可以通過篡改 Cookie 來達(dá)到他們的目的。所以,把 Cookie 的值設(shè)置得“通俗易懂”不是一個(gè)好主意。例如,我們要用 Cookie 來保存登陸的用戶名,如果單純把這個(gè)用戶名存入 Cookie,那么攻擊者會(huì)很容易通過修改成他人的用戶名來偽造 Cookie 登陸。所以,我們需要其他的手段來防止這一點(diǎn)。

我的做法是,用戶注冊的時(shí)候,把用戶名按一定手段進(jìn)行變換,然后使用 SHA() 函數(shù)加密生成一個(gè)用戶密鑰,然后把這個(gè)密鑰儲(chǔ)存進(jìn)數(shù)據(jù)庫。登陸時(shí),再把這個(gè)密鑰存儲(chǔ)到 Cookie 中,通過檢查 Cookie 中的密鑰和數(shù)據(jù)庫中用戶密鑰的匹配情況判定是哪位用戶登錄。這樣,只要你的用戶名變換方法不泄露,攻擊者就很難按他們的想法偽造 Cookie。

使用 GET 方法

在網(wǎng)頁間傳遞信息除了剛才介紹的 POST 方法外,還有 GET 方法。GET 方法是通過 URL 來完成信息傳遞的。例如,構(gòu)造下列網(wǎng)址:

http://www./index.php?id=2

網(wǎng)址最后有 ?id=2 標(biāo)記。這個(gè)信息會(huì)儲(chǔ)存在 $_GET['id'] 這個(gè)超級全局變量中,并且可以在 PHP 中使用:

<?php

    if (isset($_GET['id'])) {
        $id = $_GET['id'];
        // code goes there
    }

?>

這個(gè)例子中我們把 2 賦給了變量 $id。當(dāng)然,也可以構(gòu)造這樣的網(wǎng)址:

http://www./index.php?id=2&message=10

除了多一個(gè)可以使用的 $_GET['message'] 以外沒有任何不同。

這個(gè)特性的用處之一就是可以根據(jù)網(wǎng)址的不同,配合數(shù)據(jù)庫查詢,返回不同的網(wǎng)頁內(nèi)容。例如我做的在線問答系統(tǒng),就是根據(jù) problem_id 來返回不同題目的。

注意,由于 GET 方法的數(shù)值是不可靠的(用戶可以手動(dòng)構(gòu)造 URL 來傳遞他們想要的參數(shù)),所以應(yīng)該僅僅用它來做一些無關(guān)痛癢的事情(例如顯示不同的頁面內(nèi)容)。這里我并沒有強(qiáng)調(diào) GET 方法的數(shù)值是“透明”的:雖然 POST 方法的數(shù)值不會(huì)顯示在 URL 中,但是它還是會(huì)通過 HTTP Header 發(fā)送到服務(wù)器,用許多插件和小工具都可以查看 HTTP Header 信息。

另外,如果你的表單是用來上傳文件的,那么估計(jì)你會(huì)更喜歡 POST 方法:因?yàn)? GET 方法得到的 URL 可能會(huì)很長,甚至超過瀏覽器的限制!

POST 和 GET 方法混用

設(shè)計(jì)較為復(fù)雜的頁面的時(shí)候,我們往往需要在一個(gè)頁面內(nèi)同時(shí)處理 POST 和 GET 方法的數(shù)據(jù)。例如一個(gè)答題頁面,需要 GET 方法來獲取題目信息,同時(shí)需要 POST 方法來把用戶的回答儲(chǔ)存到數(shù)據(jù)庫中。同時(shí),這個(gè)頁面可能還需要針對不同的回答給出不同的反饋。

要處理這個(gè)問題,只需要理解并用好 isset() 函數(shù)即可。例如,通??梢赃@樣組織頁面:

<?php

    if (isset($_GET['id'])) {
        $id = $_GET['id'];
    } else if (isset($_POST['id'])) {
        $id = $_POST['id'];
    } else {
        echo "fatal error";
    }

    // use $id to fetch problem information
    // and display the problem

    if (isset($_POST['submit'])) {
        // check and process
    }

?>

使用模板

最后一部分,來講一下使用模板構(gòu)造一個(gè)網(wǎng)站。

事實(shí)上,網(wǎng)站的每個(gè)頁面中,有許多部分是完全相同的,例如每一頁的 header 和 footer 部分。這樣,我們沒必要在每一頁內(nèi)寫相同的代碼。除了麻煩和浪費(fèi)空間以外,還有一點(diǎn)很重要的原因,就是修改的時(shí)候工作量很大。

PHP 中 require_once() 函數(shù)的作用就是把其他文件的全部插入此處。例如:

<?php

    require_once('define.php');

?>

會(huì)把 define.php 中的內(nèi)容插入當(dāng)前位置。所以,我們可以建立一個(gè) header.phpfooter.php,寫好頁面的頭部、底部之后在每個(gè)其他頁面導(dǎo)入就可以了。另外,用于判斷用戶是否已登錄的語句、用于定義數(shù)據(jù)庫信息的語句等都用該使用模板,而不是在每一頁分別加入。

PHP的錯(cuò)誤處理

分級的錯(cuò)誤信息

最后來講一下 PHP 的錯(cuò)誤處理機(jī)制。如果你寫了有錯(cuò)誤的 PHP 代碼,那么運(yùn)行的時(shí)候系統(tǒng)會(huì)自動(dòng)生成一些錯(cuò)誤提示信息并且打印到屏幕上,以提醒用戶修復(fù)。通常,這些錯(cuò)誤信息是分級的。首先,是 notice。如果屏幕出現(xiàn)了 notice: (...) 的提示說明你有需要修復(fù)的小問題(你沒有完全按照規(guī)則進(jìn)行),不過問題不大,代碼還是會(huì)繼續(xù)執(zhí)行完畢。而 warning 則更嚴(yán)重一些,如果出現(xiàn) warning,你可能需要思考一下你是否真的知道自己在做什么,并作出修改。但是,程序仍然會(huì)運(yùn)行。如果出現(xiàn)了 error,那么 PHP 是在跟你說:你是個(gè)白癡;這種代碼無法執(zhí)行,程序的運(yùn)行會(huì)中止。

在寫 PHP 程序的時(shí)候,我們需要這些錯(cuò)誤提示來幫助我們改正錯(cuò)誤,但是當(dāng)產(chǎn)品發(fā)布的時(shí)候,開發(fā)人員往往傾向于隱藏錯(cuò)誤提示:用戶收到這些信息是很讓人惱火的,而且,讓他人知道你的代碼有什么漏洞總歸不是一個(gè)好主意,因?yàn)檫@可能被某些圖謀不軌的攻擊者加以利用。

Suppression Operator

有時(shí),為了代碼的簡潔性考慮我們可能會(huì)故意犯一些無關(guān)痛癢的小錯(cuò)誤。例如,如果 $_GET 中的某一個(gè)元素不一定總會(huì)被提交到 PHP,那么理論上應(yīng)該使用 isset() 函數(shù)來進(jìn)行檢測。但是,如果你覺得到處使用這個(gè)函數(shù)太麻煩了,可以省略 isset() 函數(shù)而直接使用這個(gè)元素,只不過如果它沒有被設(shè)置的話會(huì)返回一個(gè) notice 錯(cuò)誤信息(類似于 C/C++ 中的變量未聲明)。這時(shí),為了忽略這一條信息,可以使用錯(cuò)誤抑制操作符 @。例如:

<?php

    if (@$_GET['opt']) {
        // code goes here...
    }

?>

其他提示

  • 盡管沒有特別強(qiáng)調(diào),但是有幾個(gè)函數(shù)是需要靈活掌握和使用的,例如 exit()。它可以立刻結(jié)束 PHP 程序的運(yùn)行。例如,有的頁面需要一定用戶權(quán)限才能訪問,則可以把驗(yàn)證權(quán)限的代碼放在頁面頂端,如果驗(yàn)證失敗則顯示錯(cuò)誤信息并調(diào)用 exit() 函數(shù)。
  • 當(dāng)一個(gè) SQL 連接的使命完成后,不要忘了用 mysqli_close() 關(guān)閉它。
  • 設(shè)計(jì) SQL 數(shù)據(jù)庫的結(jié)構(gòu)是一件非常重要的事情,設(shè)計(jì)的原則是高效且便于查詢。一旦你的數(shù)據(jù)庫充滿各種信息,再想更改它的結(jié)構(gòu)就會(huì)變得有些困難。
  • SQL 的知識(shí)這里介紹得不多。它有許多特性,比如默認(rèn)值、主鍵等。默認(rèn)值的意思是如果不設(shè)定,那么該字段采用默認(rèn)值;主鍵則規(guī)定該字段每行是不能重復(fù)的。默認(rèn)值除了固定字符以外,還可以設(shè)定為時(shí)間,甚至自增。例如,要建立一個(gè)用戶數(shù)據(jù)庫,為每個(gè)用戶分配一個(gè)唯一 ID,則可以把數(shù)據(jù)庫中的 ID 字段設(shè)為 AUTO INCREMENT,這樣每次不用手工維護(hù)這個(gè)字段,只要新增一行,這個(gè)字段的數(shù)值就增 1(默認(rèn)從 1 開始),很方便。 一般會(huì)把這種 ID 字段設(shè)為主鍵。
  • 本來打算簡要介紹一下 PHP 和 MySQL 的,但是一寫就是 7000 字。即使如此,本文介紹的所有特性也僅僅是構(gòu)建一個(gè)動(dòng)態(tài)網(wǎng)站最基本的知識(shí),而且許多非重要的知識(shí)并沒有介紹。你應(yīng)該通過書籍更深入地學(xué)習(xí)。
  • 額外推薦 PHP 的文檔,對新手非常友好,值得一看:http://us3./manual/en/

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    九九热精品视频在线观看| 午夜国产精品福利在线观看| 亚洲精品一二三区不卡| 日韩人妻中文字幕精品| 午夜福利视频日本一区| 欧美一级不卡视频在线观看| 欧美一区二区黑人在线| 黄片三级免费在线观看| 91麻豆视频国产一区二区| 人妻少妇久久中文字幕久久| 国产麻豆精品福利在线| 免费观看成人免费视频| 欧美一区二区在线日韩| 久久亚洲成熟女人毛片| 欧美日韩高清不卡在线播放| 国产欧美日韩一级小黄片| 欧美av人人妻av人人爽蜜桃| 欧美国产极品一区二区| 亚洲国产天堂av成人在线播放| 在线观看免费午夜福利| 美女被后入福利在线观看| 精品日韩欧美一区久久| 精品久久综合日本欧美| 亚洲午夜福利视频在线| 国产一区欧美午夜福利| 国产精品亚洲综合色区韩国| 国产日韩精品激情在线观看| 人妻久久这里只有精品| 精品国自产拍天天青青草原| 色无极东京热男人的天堂| 日韩美女偷拍视频久久| 深夜日本福利在线观看| 白丝美女被插入视频在线观看| 国产欧美亚洲精品自拍| 国产精品人妻熟女毛片av久| 亚洲综合伊人五月天中文| 午夜国产成人福利视频| 中文字幕人妻av不卡| 日韩无套内射免费精品| 三级高清有码在线观看| 日本一品道在线免费观看|