内容提要 [隐藏] 由于学校社团活动需要一个支持数据库和用户提交信息的动态网站,所以花了几个晚上速成了一下 PHP 和 MySQL 的相关知识,之后就写了一个网站,结果表明运行的很顺利。之前感觉这种动态网站很深奥,以为会很难,可是没想到如果只实现不太复杂的功能的话,其实并不很难。 我争取用最浅显易懂的语言来说明一下是如何实现的。阅读本文需要简单的 HTML 基础知识和(任一编程语言的)编程基础知识(例如变量、值、循环、语句块的概念等)。 PHP 基础概述PHP 是一种解释性语言,可用于对网页进行预处理。PHP 脚本在服务器端运行,其运行结果是一个可用来显示的网页。尽管可以完成许多类似工作,但是 Javascript 和 PHP 的一大区别就是,Javascript 是在浏览器端运行的。事实上,浏览器会接收 Javascript 代码,并运行它,所以理论上用户是可以检查 Javascript 代码的。而 PHP 不会将原始代码交给浏览器, 只会将其运行的结果交给浏览器,所以用 PHP 处理用户登陆、用户权限等问题是安全可靠的。 PHP 与 HTML实际编写的时候,通常采用的方式是建立扩展名为 规则:php 代码需要包含在 <?php // code goes here ?> 提示:这是一个 php 和 html 混编的较为生动的例子。 <?php if ($var == "true") { ?> <html id="ie6"> <?php } else { ?> <html id="ie8"> <?php } ?> 这里的意思是,如果 php 中的变量 关于 PHP 中的操作符PHP 采用的操作符和 C/C++ 是类似的,例如用 关于 PHP 中的变量PHP 中变量的命名一律以符号 关于 PHP 中的语句这一点 PHP 和许多其他常见的编程语言很类似,也可以用 MySQL 基础使用 MySQL 数据库是存储数据的一种方法,MySQL 需要和 PHP 配合来完成对数据库的查询(这里术语“查询”包括写入、更新、读取等)操作。利用 MySQL,你可以创建许多数据库(database),每个数据库可以包含多个表(table),而每个表包含若干字段。为了高效,一般会采取分类维护多个表的方式,而不是把所有数据都储存在同一个表中。 MySQL 需要服务器支持。使用的第一步是建立一个数据库,可以用相应的图形化工具(例如 phpMyAdmin)来建立数据库,也可以在终端直接使用下列 SQL 语句来创建一个名为 database_name 的数据库: CREATE DATABASE database_name; 创建好数据库之后,需要创建表。可以通过下列 SQL 语句来创建一个名为 table_name 的表: USE database_name; CREATE TABLE table_name ( first_name varchar(30), last_name varchar(30), ); 第一句说明在哪个数据库中添加表,第二个说明添加的表的详情。这里我们在表中添加了两个字段,分别叫做 其他常见的数据类型如下: VARCHAR(100) --可变字符 CHARACTER(1) --定长 INTEGER --整数 DECIMAL(10, 2) --小数(小数点前后的位数) TIMESTAMP --日期和时间 DATE --日期 你可能已经看出来了,MySQL 的注释符为 使 PHP 和 MySQL 协作第一种方式现在你已经创建好了 SQL 数据表,并对 PHP 语言有了一个概览。下面我们直奔主题,学习如何对数据表进行查询。 为了使 PHP 和 MySQL 进行交互,需要为 PHP 提供你的数据库用户名、密码以及数据库名、数据表名,当然,最重要的,查询操作的 SQL 语句。我们一一来观察是如何实现的。 <?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); ?> 下面来解释一下这一坨代码的工作原理。
事实上,如果把这些代码保存成一个网页,当用户打开网页的时候,如果各项参数正确,它就会完整地运行下去。 这里的 SQL 语句的含义是向叫做 INSERT INTO table_name (column_name1, column_name2, ...) VALUES ('value1', 'value2', ...) 如果你要做的仅仅是执行一个 SQL 语句,那么使用这种模式就可以完成。提醒一下, 另一个常用的 SQL 语句就是修改某一行。它的形式为: UPDATE table_name SET column_name1='preferred_value1', column_name2='preferred_value2', ..., WHERE id = '$id' 当然,这个语句应该是写到一行的,不过为了清晰我分开来写。它的含义是,修改名为 注意:会修改所有符合 WHERE id = '$id' AND is_male = 'true' WHERE id = '$id' OR pid = '$id' 都是合法的(如果你想问 下面介绍其他 SQL 语句。 --更新table_name表中的数据 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表添加一个叫age的字段,类型为 INTEGER ALTER TABLE table_name ADD COLUMN age INTEGER --把table_name表中的col字段改名为loc,并设类型为 INTEGER ALTER TABLE table_name CHANGE COLUMN col loc INTEGER 可见,第一种方式的本质就是编写一条 SQL 语句,然后通过 PHP 来执行它。下面,我们来看第二种方式。 第二种方式有时,我们不满足于让服务器去执行一条 SQL 语句。我们会需要从数据库中查询信息,然后把得到的信息储存起来(其实就是储存在变量中)。这样,我们需要一些额外的工作。先看一坨代码: <?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']; ?> 这里我们省略了 这一坨代码和上一坨的主要区别是,我们使用了 解释一下 然后,用变量 到这里你应该问一个问题:如果满足 最后补充一点刚才没有提到的。如果不需要所有字段的数据,可以只选择需要的字段。方法是把原来 SQL 语句中的通配符换成字段名称。例如: SELECT problem_name, problem_type FROM table_name WHERE problem_id='$id' while 循环在 PHP 中的应用举例如果我们要把一个数据库的许多行信息都展示在网页中,那么需要用到 <?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>"; } ?> 如果有一定编程基础的话上面的代码很容易看懂。上面新出现了三种用法,说明如下:
SELECT user_id, user_rank FROM table_name ORDER BY user_rank DESC, user_id ASC
从表单获取信息概述这一部分我们演示如何构建一个表单,使用户填写这个表单并把内容储存到数据库。这一技术是用户注册系统和用户互动的基础。 要实现这个功能,需要 HTML 和 PHP 配合完成。HTML 负责表单,而 PHP 负责获取信息并使用 SQL 查询储存信息。首先来看 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 属性后面的 下面来看一下相应的 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); ?> 首先仍然是建立数据库连接。当用户点击 sumbit 按钮后,表单的内容会被储存在 PHP 中 这里新出现了一个内容,就是 需要注意的是,这仅仅是最简单的代码,而且实际上是不完善的。如果要真正投入使用,我们需要使它更健壮一些。下面逐一讨论这些内容。 检查用户输入是否合法如果用户根本没有填写表单,就直接点击提交按钮,会发生什么?在上面的实例中,PHP 依然会乖乖地把空内容插入,而这显然是垃圾信息,不是我们需要的。所以,需要在插入前检查被插入的变量是否为空。例如: <?php if (!empty($user) && !empty($info)) { // 插入操作 } ?> 这里出现了 错误提示用户输入有误时,上面的改进除了不执行SQL查询,并没有多少直观上的变化。用户不会收到任何信息表明他们的填写是不合适的。所以我们要在这时产生一些提示,引导用户正确填写表单。 <?php if (!empty($user) && !empty($info)) { // 插入操作 } else { echo "请填写全部内容后再提交"; } ?> 防范 SQL 注入攻击我们执行的 SQL语句中包含变量,执行的时候会直接把变量内容替换进去。而如果攻击者在输入框中输入一些危险的字符(通常包含 SQL 注释符 <?php $user = mysqli_real_escape_string($dbc, trim($_POST['username'])); $info = mysqli_real_escape_string($dbc, trim($_POST['info'])); ?> 粘性表单如果用户第一次填写失败,他们希望能保留已经填写好的内容,只做些修改就好了。这需要使用粘性表单技术。要实现,只需要稍稍改动 HTML 表单部分的代码: <label for="username">用户名:</label> <input type="text" id="username" name="user" value="<?php if (!empty($user)) echo $user; ?>" /> 显而易见,如果用户填写后因为某些原因没有提交而是回到了这个表单,并且之前填写了 user 字段的内容,那么此时 构造一个注册页面虽然上面说了很多,但是仅仅满足了我们最基本的输入要求。许多时候我们需要更为复杂的功能。举例来说,要写一个注册页面,必须检查用户名是否重复,还要对密码采取某种技术加密以保证安全。 检查用户是否重复基本原理就是,根据需要判重的字段(例如用户名)去数据库搜索。如果发现结果则用户名重复,如果没有找到则允许注册。需要一个新函数 <?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) { // 把内容插入数据库 echo "注册成功"; mysqli_close($dbc); exit(); } else { echo "用户名已被占用,请重新选择用户名"; $user = ""; } ?> 把 需要说明的是 对密码进行加密存储明文存储密码是对用户很不负责的,不仅数据库管理员可以看到密码,一旦数据库泄漏,密码就会被公开。所以,我们应该加密存储用户密码。在 PHP 中,可以使用 加密的原理是,用户输入密码后,利用 PHP 把 hash 过的密码储存在数据库中。用户登陆的时候,把用户输入的密码进行 hash 运算,之后和数据库中的进行比对。 使用方法如下: SHA('$password') 其他要素对于注册页面,不要忘记确认密码,即让用户输入两次,比较确定相等后再执行注册。 识别用户登录:Cookie只注册没有用,必须添加登录功能。登录功能可以使用 Cookie 来实现。这里假定你已经了解 Cookie 的基础知识,只说如何实现。 设置 Cookie<?php setcookie('user_type', $user_type, time() + (60 * 60 * 24 * 30)); ?> 上面的代码用来设置 Cookie,其中函数的第一个参数为 Cookie 名称,第二个参数为数值(这里用一个变量传递),第三个参数为过期时间,单位秒。示例为一个月。 可以用设置多个 Cookie 来存储许多内容,例如用户 ID、用户组(管理员还是普通用户)等。 验证 Cookie用户登陆后,我们可以设置一个 Cookie 来存储登录信息(即哪个用户登陆的),然后通过检查这个 Cookie 来设定相应功能。 <?php if (isset($_COOKIE['user_type'])) { $user_type = $_COOKIE['user_type']; } ?> 删除 Cookie要删除 Cookie,只需要把过期时间设定在过去。 <?php setcookie('user_type', '', time() - 3600); ?> 不要问我为什么设定在过去一个小时,设定几个小时都没问题。 Cookie 的安全性设置 Cookie 有其潜在的危险。由于 Cookie 是保存在用户本地的,所以用户完全可以通过篡改 Cookie 来达到他们的目的。所以,把 Cookie 的值设置得“通俗易懂”不是一个好主意。例如,我们要用 Cookie 来保存登陆的用户名,如果单纯把这个用户名存入 Cookie,那么攻击者会很容易通过修改成他人的用户名来伪造 Cookie 登陆。所以,我们需要其他的手段来防止这一点。 我的做法是,用户注册的时候,把用户名按一定手段进行变换,然后使用 使用 GET 方法在网页间传递信息除了刚才介绍的 http://www./index.php?id=2 网址最后有 <?php if (isset($_GET['id'])) { $id = $_GET['id']; // code goes there } ?> 这个例子中我们把 http://www./index.php?id=2&message=10 除了多一个可以使用的 这个特性的用处之一就是可以根据网址的不同,配合数据库查询,返回不同的网页内容。例如我做的在线问答系统,就是根据 注意,由于 另外,如果你的表单是用来上传文件的,那么估计你会更喜欢 POST 和 GET 方法混用设计较为复杂的页面的时候,我们往往需要在一个页面内同时处理 POST 和 GET 方法的数据。例如一个答题页面,需要 GET 方法来获取题目信息,同时需要 POST 方法来把用户的回答储存到数据库中。同时,这个页面可能还需要针对不同的回答给出不同的反馈。 要处理这个问题,只需要理解并用好 isset() 函数即可。例如,通常可以这样组织页面: <?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 } ?> 使用模板最后一部分,来讲一下使用模板构造一个网站。 事实上,网站的每个页面中,有许多部分是完全相同的,例如每一页的 header 和 footer 部分。这样,我们没必要在每一页内写相同的代码。除了麻烦和浪费空间以外,还有一点很重要的原因,就是修改的时候工作量很大。 PHP 中 <?php require_once('define.php'); ?> 会把 PHP的错误处理分级的错误信息最后来讲一下 PHP 的错误处理机制。如果你写了有错误的 PHP 代码,那么运行的时候系统会自动生成一些错误提示信息并且打印到屏幕上,以提醒用户修复。通常,这些错误信息是分级的。首先,是 在写 PHP 程序的时候,我们需要这些错误提示来帮助我们改正错误,但是当产品发布的时候,开发人员往往倾向于隐藏错误提示:用户收到这些信息是很让人恼火的,而且,让他人知道你的代码有什么漏洞总归不是一个好主意,因为这可能被某些图谋不轨的攻击者加以利用。 Suppression Operator有时,为了代码的简洁性考虑我们可能会故意犯一些无关痛痒的小错误。例如,如果 <?php if (@$_GET['opt']) { // code goes here... } ?> 其他提示
|
|