PHP7 和 MySQL8 实践指南(一)

原文:Practical PHP 7, MySQL 8, and MariaDB Website Databases

协议:CC BY-NC-SA 4.0

一、创建并测试数据库和表

本章介绍了数据库的概念和测试数据库的实用方法。使用这些示例,您将创建一个 MariaDB 或 MySQL 数据库和一个表。通过学习这些示例,您将会熟悉数据库管理界面。

完成本章后,学生将能够

  • 定义和设计数据库和表

  • 安装和使用 WAMP 软件包

  • 使用 phpMyAdmin 创建一个数据库和表

  • 使用用户 ID 和密码保护 phpMyAdmin 和数据库

  • 删除数据库和/或表

数据库可以用来存储产品、客户的详细信息、社团或俱乐部成员的记录等等。它们可以存储姓名、密码、地址、电子邮件地址、注册日期、博客条目和电话号码。数据库可以被看作是包含数据表的文件夹。数据表有;数据库表中的行被称为记录。表 1-1 显示了一个典型的数据库表。

表 1-1

典型的数据库

|

用户 id

|

名字

|

姓氏

|

电子邮件

|

密码

|

电话

|
| — | — | — | — | — | — |
| one | 凯文 | 烧水用水壶 | kev@kettle.com | K3ttl3fur | 305 111 1111 |
| Two | 苏珊(女子名) | 炖锅 | sue@kitchen.org.uk | n @您的 5 | 01111 222 1111 |
| three | 奥利弗 | 烤箱 | oliver@cokker.co.uk | H0tst0v3 | 03333 111 4444 |

定义开发人员、管理员和用户

在本书中,术语开发者(又名站长)指的是设计和制作数据库的人;他们将把数据库整合到一个网站上。有时可能会用到术语网站管理员网页设计师。用的时候一般和开发者是一个意思。在本书的一些教程中,管理员会员秘书这两个词的意思相同,这些教程都是基于为一个俱乐部建立数据库。单词管理员表示负责监控和维护数据库表内容的人员。显然,一个人可以既是开发人员又是管理员。然而,大多数开发人员会维护数据库的结构,但不想要修改和删除记录的麻烦;这应该是管理者的角色(比如俱乐部或社团的会员秘书)。

用户是查看网站数据库并可能与之交互的任何普通公众成员。出于安全原因,用户对数据库的访问极其有限;然而,他们将被允许注册会员,登录到一个特殊的部分,或改变他们的密码。

警告

委托开发数据库的组织必须遵守数据库开发所在国的法规和法律。在英国,必须遵守数据库开发地区的数据保护法。如果这些数据将被用于盈利,这一点尤为重要。这可能需要获得许可证。此外,开发人员和管理员通常必须签署一份文件,确认他们永远不会披露数据库中记录的个人详细信息。英国信息专员办公室(ICO)要求根据拥有该数据库的组织的收入支付年度许可费。美国没有相应的法律,但是各州的隐私法可能有所不同。您必须了解并遵守客户所在地区的数据保护法。

可供多个国家访问的数据库将需要满足每个国家的要求。有时这需要创建不同版本的数据库和/或网站,否则就不需要。许多法律法规落后于提供尽可能安全的数据库环境的总体需求。在设计和使用数据库时,开发人员应该总是选择“最安全”的方法。仅用于教育目的的数据库(如本书中所示的数据库)不必满足要求和法规(如许可)。

定义交互式网站

交互式网站通常被称为动态网站;然而,这本书使用了互动这个词,因为动态可以表示很多事情。例如,它可能意味着感人的,强大的,引人注目的,浮华的,令人兴奋的。对于初学者来说,这些含义都不能定义与用户交互的网页。

动态(Dynamic)通常用来表示令人兴奋,但在交互式注册表单中几乎看不到兴奋。动态也是一个音乐术语,意思是响度或速度的改变或变化。如果动态可以指变化,为什么动态模板被设计成从一个网页到另一个网页提供一致性?术语交互有一个明确的含义,从现在开始将在本书中使用。

MariaDB 和 MySQL(使用 PHP)允许用户和管理员使用网站页面与数据库进行交互。例如,用户可以通过网站上的注册页面注册成为某个组织的成员。用户将能够为成员资格表提供他们的个人数据。数据库管理系统然后自动将用户的输入输入到管理员的表格中;这减轻了管理员的工作量。网站的注册页面可以被编程来过滤用户的数据输入并进行验证。在交互式页面上,用户甚至可以更新自己在数据库中的记录。

交互性意味着管理员的工作量大大减少,但不是完全减少。例如,如果数据库用于书店,管理员仍然需要输入任何新的书名和价格。另一方面,当某本书的库存需要补充时,交互式数据库可以被编程来提醒管理员。

在第二章,你将学习开发一个简单的交互式网站。

仅对交互式数据库表使用 MariaDB 或 MySQL

非交互式数据表意味着只有管理员才能输入或修改表中的信息。使用电子表格(如 Microsoft Excel)更容易创建和管理非交互式数据表。然而,网站用户不能与这样的数据表进行交互。使用 MariaDB 或 MySQL 数据库管理系统(DBMS)来创建数据表的非交互式(静态)版本就像用大锤砸坚果一样。网站用户没有输入,也不能搜索或更新数据。

对非交互式版本使用 MariaDB 或 MySQL DBMS 不会减少管理员的工作量;他们必须输入所有成员的数据,并验证这些数据是真实的。

注意

一些交互式网页不需要数据库就能运行。例如,“联系我们”表单可以被认为是交互式的,因为它接受用户的输入,并通过电子邮件经由 PHP 表单处理程序将其传输给网站所有者;这可以在没有数据库的情况下轻松实现。在本书中,术语交互总是指用户可以与数据库交互。

开发和维护数据库的方法

管理数据库的四种方法如下(最简单的方法在前,最难的在后):

  • phpMyAdmin(或其他管理工具)

  • 服务器端编程语言(Professional Hypertext Preprocessor 的缩写)

  • SQL 脚本

  • 命令行

在本书中,我们将主要使用前两种方法(phpMyAdmin 和 PHP)。我们将使用 SQL 脚本和命令行来简要演示如何创建、更新和分发您的数据库。对于交互式数据库,您将需要一些 PHP 文件。在创建交互式数据库之前,您不需要掌握大量的 PHP 知识。我们将在每个项目的适当位置介绍您需要的 PHP 语法——也就是说,在上下文中。一步一步,充分工作的例子将向您展示 MariaDB 和 MySQL 可以做什么以及如何做。

由于图形用户界面(GUI)的普及,已经开发出图形用户界面来促进开发和维护数据库的任务。这些管理工具是开发包的一部分,开发包包括 web 服务器(Apache)、数据库(MariaDB 或 MySQL)和编程语言(PHP)。在本书中,我们将介绍两个最流行的软件包:XAMPP 和 EasyPHP。

简要了解 Web 服务器通信

数据库需要一个服务器、一个 DBMS 和一个 PHP 处理器,如图 1-1 所示。这些可以作为一个已经配置好的一体化软件包下载。本书中项目的测试和开发基于免费的 XAMPP 和 EasyPHP 包。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-1

Web 服务器通信

图 1-1 显示了 XAMPP 和 EasyPHP 开发平台内置的主要组件。它们如下:

  • Apache 是大多数主机使用的网页服务器,用于本地主机(用户计算机)上的 web 开发。Web 服务器通过查看文件结尾()来确定网页是否包含 PHP 代码。php 。如果 PHP 代码存在,代码被传递给 PHP 解释器执行。如果 PHP 解释器确定 PHP 程序中存在与数据库相关的代码或 SQL,这些信息将被传递给 MySQL 或 MariaDB 数据库管理系统。数据库管理系统执行 SQL 语句并将结果返回给 PHP 解释器。PHP 解释器完成 PHP 代码的执行,并将结果返回给 Apache 服务器。Apache 服务器收集 PHP 代码的结果,以及任何 HTML、CSS 和/或 JavaScript,并将这些信息发送到用户机器上的 web 浏览器。网络浏览器然后执行 HTML、CSS 和/或 JavaScript 代码。

  • MySQL 和 MariaDB 不仅仅是数据库;它们也是数据库管理系统。这些包包括创建、管理和保护数据库的工具。

  • PHP 处理器解释并执行任何 PHP 代码。如果有语法(编码错误)、逻辑错误或系统错误(内存不足),它将抛出错误或异常。

  • phpMyAdmin 是一个基于 GUI 的管理工具,用于创建和维护数据库及其表。

一个单一的一体化软件包,如 XAMPP 或 EasyPHP,包含图 1-1 中提到的四个程序,被称为 WAMP (Windows、Apache、MySQL/MariaDB 和 PHP)。在 WAMPs 中,主要组件是预先配置好的,这样它们就可以相互通信。在苹果操作系统上相当于 MAMP,在 Linux 电脑上相当于 LAMP。

文件夹 htdocs (或 eds-www )是你的网页的默认存储和可执行区域。默认情况下,Apache 会在 htdocs (或 eds-www )中查找你的网页。这些页面可以被设计成允许用户与数据库交互。其他页面将在用户看不见的情况下运行,在浏览器和数据库之间来回传输信息。页面通常是 HTML 和 PHP 文件或者两者的组合。

警告

图 1-1 中的所有东西可能都已经安装在远程主机上,但是在学习的过程中,千万不要使用远程主机来创建数据库。出于安全原因,在熟练掌握之前,不要使用远程主机。我们建议您在自己的计算机上使用 all-in-one 软件包学习和开发数据库。注意,安装在自己电脑上的一体机包纯粹是开发工具。该数据库在开发和彻底测试后,最终可以上传到主机,供用户使用。

用于测试的免费开发平台

您将无法以正常的方式测试您的工作——也就是说,通过使用浏览器来查看位于您硬盘上的数据库和 PHP 代码。当浏览器请求包含 PHP 的网页时,PHP 代码在 web 服务器中执行。代码的执行结果将显示在浏览器中。如果您查看浏览器代码(右键单击网页并选择 View Source),您将看不到 PHP 代码。您将看到代码执行产生的结果。这让那些习惯于在浏览器中看到自己创建的任何 HTML、CSS 或标准 JavaScript 代码的初级开发人员感到困惑。记住,HTML、CSS 和标准 JavaScript 都是在浏览器中执行的。PHP 代码在 web 服务器中执行,即使该服务器在您自己的 PC 上。

在您的计算机上使用 all-in-one 软件包将允许您查看您创建的所有代码以及在您的 PC 上执行这些代码的结果。这本书假设你在学习的时候会在你自己的电脑上使用一个软件包,并用于开发未来的数据库驱动的网站。

注意

在当前黑客试图破坏或收集个人、公司和政府数据的世界中,互联网托管网站上的所有网页必须得到保护。本书中最早的项目必然是简单的,并且有一些安全的特性。但是,不建议您将这些页面上传到互联网主机。当你获得了经验和信心,并且你确信你了解如何保护网站和数据库的安全,你可以改编这本书的后期项目用于你自己的网站,然后将它们上传到远程主机。

在自己的电脑上使用 XAMPP

XAMPP 软件包是免费的,并且已经过预配置,因此组件可以相互通信。这消除了通常下载几个单独的组件,然后配置它们一起工作的麻烦。XAMPP 包括 Windows (WAMP)、Linux (LAMP)和苹果(Mac)操作系统(MAMP)的软件包。本书中的示例是使用 WAMP 环境开发的。然而,这些例子也应该在灯或 MAMP 环境中工作。

在撰写本文时,XAMPP 的最新版本是 7.2.4。整本书都使用这个版本。它的组件版本如下:Apache 2.4.33、MariaDB 10.1.25、PHP 7.2.4 和 phpMyAdmin 4.8.0,以及其他工具。本书中的例子并不完全兼容 7.0 之前的 PHP 版本。

注意

如果您安装的版本比本书中演示的版本新,安装步骤可能会略有不同。您应该参考 XAMPP 网站( www.apachefriends.org )或在互联网上搜索安装说明(尝试 youtube.com )以获得本书中未演示的任何版本。如果新版本包含重大升级(例如 PHP 8),可能需要对一些代码进行更改。查看 Apress 网站,了解与主要版本发布相关的代码变更。

在我们给你下载 XAMPP 的指导之前,我们需要解决一个困扰每个初学者的问题,这个问题是关于将一个开发的数据库从 XAMPP 或 EasyPHP 转移到远程主机。如果您在自己的计算机上使用这些软件包中的一个,将会出现一个问题,如下一节的标题所述:

我能把数据库从 XAMPP 或 EasyPHP 转移到远程主机吗?

困扰初学者的主要问题是:“如果我在本地 WAMP 上开发一个数据库,我能轻松地将它迁移到远程主机上吗?”初学者完全有理由担心,因为大多数手册很少给出关于这个主题的提示。然而,答案是这样的:“是的,您将能够移动数据库。”你会在本书后面找到完整的说明。

现在我们将提供下载和安装 XAMPP 的信息。

警告

如果你想安装多个免费的 WAP,可以在同一台电脑上安装 EASYPHP 和 XAMPP。但是,在打开另一个之前,请确保其中一个已关闭;否则,它们将争夺相同的端口,并导致令人讨厌的问题。

下载并安装 XAMPP

XAMPP 需要最少的配置。要下载该软件包,请前往 www.apachefriends.org/

点击主页面顶部菜单中的下载,如图 1-2 所示。下载页面将显示当前可用的版本。如果可以的话,选择 XAMPP 的 7.2.4 版本,以确保你在本书中使用的所有例子都是兼容的。如果有版本 7 的次要版本(比如 7.3.0),它可能仍然是兼容的。如果版本 7 不可用,请下载最新版本。然后查看 Apress 网站,看看本书中的例子是否需要修改代码。下载页面会不时变化,因此您可能需要浏览页面以找到显示的版本。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-2

安装 XAMPP

注意

下载页面将说明您必须安装 C++运行时库。XAMPP 及其相关工具是使用 C++创建的。如果您使用的是当前版本的 Windows,那么您可能已经有了正确版本的 C++。但是,如果您收到一条错误消息,指出它已丢失,请按照错误消息提供的链接下载正确的版本。

单击最新版本旁边的下载按钮。这将开始下载过程。该文件将自动下载到您电脑上的下载文件夹中。根据您使用的浏览器,可能会询问您是否要运行或保存该程序。有些浏览器会自动保存程序,并要求你点击文件来运行它。如果您不知道如何在您使用的浏览器中找到下载的文件,请在互联网上搜索相关信息。文件下载完成后,双击该文件(或根据提示单击运行选项)开始安装过程。

环境将提示您请求安装程序的权限。单击“是”继续该过程。安装程序将决定您机器上的安全设置。它可能会警告您,您的安全性可能会限制某些可用的 XAMPP 选项。您可以选择继续安装或停止当前安装并暂时关闭您的安全程序。如果关闭安全保护,请确保你的电脑不再连接到互联网。安装程序还会查看您的用户帐户控制(UAC)设置。这些设置根据您登录系统时使用的用户 ID 来限制对文件夹和文件的访问。如果您使用的是没有管理权限的计算机,安装例程将提供一条警告消息,提示您将程序安装在不同于默认位置(程序文件)的位置。按照提示进行操作,并在提示到不同位置时更改默认设置,例如 c:/XAMPP

将出现欢迎使用 XAMPP 安装向导屏幕。单击“下一步”继续安装。下一个屏幕将显示所有可供安装的选项。如果您是初学者,只需通过单击“下一步”按钮接受已经检查的项目。中级和高级用户可能会考虑删除您确定不会使用的项目。如果需要,您可以随时安装它们。下一个屏幕将显示安装位置。使用 c:/XAMPP 来避免任何可能的安全访问问题。单击下一步。下一个屏幕将试图说服你也看看安装 CMS 程序(如 WordPress)。现在,取消选中“了解更多”框。有兴趣可以以后再装。单击“下一步”按钮。下一个屏幕将显示设置就绪。单击下一步。安装将开始。默认情况下,启动控制面板复选框将被选中。保持选中状态。单击完成按钮。在控制面板显示之前,您可能会收到语言选择提示。

如果在安装过程中出现错误,复制错误信息并粘贴到搜索引擎(如 Google)中。查看建议解决方案的列表。转到一个受信任的网站,并按照指示来纠正您的错误。

XAMPP 控制面板上标有运行的项目通常会自动出现,然后您就可以停止各种模块。如果它们没有自动启动,请单击 Apache 和 MySQL 的 XAMPP 控制面板上的开始按钮。如果一个按钮显示“停止”,则该模块已经在运行。接下来呢?如果你被要求将模块作为服务运行,选择将 Apache 和 MySQL 作为服务运行,然后当你双击 XAMPP 桌面图标时,这些模块将自动启动(图 1-3 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-3

XAMPP 的标志

在桌面上为 XAMPP 的 htdocs 文件夹创建一个快捷方式,放在 XAMPP 图标旁边,如图 1-4 所示。使用这个快捷方式将你的 PHP 文件加载到 C:\xampp\htdocs 文件夹中。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-4

节省时间的快捷方式

如果在安装过程中没有创建桌面图标,我们建议您转到 C:\xampp 文件夹,然后为【xampp-control.exe】文件创建一个桌面快捷方式。

为了方便起见,将两个桌面物品并排放置,如图 1-4 所示。一个图标启动和停止 XAMPP,另一个允许你直接在 XAMPP htdocs 文件夹中创建和修改页面。

一个常见的问题是 Skype 和其他应用也使用端口 80,这是 Apache 的默认端口。如果另一个应用正在使用这个端口,Apache 将不会启动。日志(图 1-5 )将显示 Apache 是否因为端口被占用而无法启动。如果出现这种情况,请转到 XAMPP 控制面板,然后单击 Netstat 按钮。此窗口将列出您的 PC 上运行的所有应用以及它们使用的端口。确定一个未使用的端口(如 8080)并关闭 Netstat 窗口。然后单击 Apache 旁边的 Config 按钮。选择 httpd.conf 。配置文件将在记事本(或您的默认文本编辑器)中打开。点击编辑,点击搜索,输入 80 。单击查找下一行,直到找到以下行:

Listen 80

将该行更改为:

Listen 8080

或者,将其更改为您决定使用的任何端口。

现在返回到搜索并单击查找下一行,直到找到以下行:

ServerName: localhost: 80

将该行更改为:

ServerName: localhost: 8080

或者将其更改为您决定使用的端口。

保存文件,并关闭记事本(或您的文本编辑器)。转到 XAMPP 控制面板,点击页面右上角的配置按钮。单击服务和端口设置按钮。单击 Apache 选项卡。输入 Apache 的新端口(8080 或您决定使用的任何端口)。单击保存。关闭配置窗口。现在点击控制面板中 Apache 旁边的开始按钮(图 1-5 )。几秒钟后,Apache 应该会启动。这将由 Apache 后面的绿色背景表示。您还需要启动 MySQL,因为没有 Apache 的运行,它无法启动。

从 XAMPP 开始

从现在开始,要在 XAMPP 测试您的页面,双击桌面图标并检查 Apache 和 MySQL/MariaDB 是否已经启动。如果它们尚未启动,请单击 Apache 的启动按钮。启动后,点击 MySQL/MariaDB 的开始按钮,然后最小化控制面板。

图 1-5 为 XAMPP 控制面板。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-5

XAMPP 控制面板

我们建议您总是最小化控制面板,以便您有一个清晰的桌面来开始您的数据库工作。

启动 Apache 和 MySQL/MariaDB 后,您可以测试您的安装并检查所有的 XAMPP 示例和工具;为此,请在浏览器中输入以下地址之一:

http://localhost/
http://127.0.0.1/

如果您添加了端口号,请确保以如下示例之一的格式包含相同的端口号:

http://127.0.0.1:8080/
http://localhost:8080/

关闭 XAMPP

测试完数据库和 PHP 文件后,关闭 XAMPP。这将为数据库开发以外的任务释放内存。要关闭,单击任务栏上的最大化 XAMPP 控制面板,然后单击控制面板上的退出按钮,如图 1-6 所示。或者,您可以右键单击通知区域中的图标,然后单击退出。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-6

结束 XAMPP 计划

数据库及其数据的安全性极其重要。XAMPP 提供了一种使计算机上的数据库和表免受有害干扰的方法,这将在本章后面介绍。

MariaDB 和 MySQL 8 在哪里?

此时,您可能会对 XAMPP 和 MariaDB 数据库的位置感到困惑。XAMPP 的创造者确实从使用 MySQL 数据库转向使用 MariaDB 数据库;然而,我们刚才解释的一切似乎表明 MySQL 仍然是 XAMPP 的一部分。然而,事实并非如此。当创建者决定切换数据库系统时,他们只需对配置进行微小的修改就可以使用 MariaDB。由于 MariaDB 的操作与 MySQL 的免费版本没有任何不同,所以创建者决定保留 MySQL 名称。由于您可能不熟悉这个环境,这可能会令人困惑。然而,对于 XAMPP 的当前用户来说,这允许在数据库环境之间进行简单的转换。请记住,无论你在 XAMPP 哪里看到 MySQL,它都是 MariaDB。

这本书的书名包括 MySQL 8 。MySQL 8 是一个完整的专业版本,拥有最新的功能,包括商业分析工具。但是,MySQL 8 没有提供 XAMPP 或 EasyPHP,完整版也不是免费的。如果你正在为公司或政府机构开发网站,你应该考虑使用 MySQL 8(或最新版本)。任何版本的 MySQL 都可以附加到 XAMPP。您确实需要阅读 MySQL 文档,以确保您附加的版本与您安装的 Apache 和 PHP 版本兼容。添加新版本的 MySQL 属于中级技能。我们在上一章提供了指导,以帮助您做好转换的准备。

在自己的电脑上使用 EasyPHP

如果你更喜欢使用 EasyPHP,或者你是一个初学者,并且你的设计师同事已经说服你必须使用 MySQL 的早期版本(即 MariaDB 不能使用),你可以安装 EasyPHP,这是一个 WAMP 包,包括免费版本的 MySQL。它还包括 MongoDB 数据库系统,这是一个非 SQL 数据库。MongoDB 不在本书讨论范围之内。

没有理由需要安装这两个软件包。但是,如果您选择这样做,您可能需要一次运行一个,或者调整端口以便两个端口可以同时运行。一旦安装完毕,您将会看到这两个包以相似的方式工作。所有示例都已经在两种环境下进行了测试。

下载并安装 EasyPHP

要下载最新版本的 EasyPHP,请访问以下站点:

www.easyphp.org

单击主页中间的 Download 按钮,下载 easyPHP 开发服务器的非 beta 版本。本书中用于测试和运行示例的版本是 17.0,包括 PHP 7.2、Apache 2.4 和 MySQL 5.7(最新的免费版本)。如果您注意到一个新的主要版本(如 18.0),请查看本书的 Apress 网站,以了解在所示的示例中是否有任何代码更改的要求。

安装程序应该会自动启动。Windows 环境将要求您通过单击“是”按钮来允许安装。单击该按钮后,设置程序将询问您的语言偏好。接下来,它将允许您更改已安装文件的位置。如果您对自己的电脑(如公司拥有的电脑)没有管理权限,您可能无法在程序文件下安装 EasyPHP。我们建议将安装文件的位置更改为以下位置:

C:\ easy PHP-DevServer-xx(xx是你正在安装的版本比如 17)

单击下一步。安装过程将询问您是否想要创建一个桌面图标。我们建议您创建一个以方便访问。单击下一步。当前屏幕将验证您的安装位置。单击安装。安装完成后,最后一个屏幕将询问您是否要启动开发服务器。单击“完成”启动它。如果在安装过程中出现错误,将错误复制到搜索引擎(如 Google)中寻找解决方案。在提供的列表中,转到一个众所周知的网站,并尝试按照指示来修复您的错误。

启动 EasyPHP

转到系统托盘中的 EasyPHP 图标,右键单击并启动仪表板。如果仪表板无法启动,请关闭开发服务器(右键单击图标并选择 Exit),并尝试从桌面快捷方式启动它。仪表板使用端口 1111。如果您仍然不能启动仪表板,右键单击开发服务器图标,选择工具,然后打开端口控制器。验证另一个进程没有使用同一个端口。如果另一个进程正在使用该端口,请停止该进程并重复前面的步骤。

在仪表板内(图 1-7 ,点击 HTTP SERVER 下方的开始按钮。点击这个按钮应该可以启动 Apache 和 MySQL。如果服务器正常启动,“开始”按钮将变为“停止”按钮。此时,HTTP 服务器和数据库服务器都应该在标题下面显示停止按钮。如果遇到错误,尝试停止开发服务器(右键单击系统托盘图标并选择 Quit)并重新启动它(单击桌面上的图标)。如果您仍然有错误,请将错误复制并粘贴到搜索引擎(如 Google)中,以查看可能的解决方案列表。从一个已知的站点选择一个建议的解决方案,并尝试按照指示去做。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-7

EasyPHP 开发服务器

关闭 EasyPHP

每当您需要关闭 EasyPHP 时,只需通过进入系统托盘,右键单击图标并选择 Quit 来关闭开发服务器。这将关闭所有与 EasyPHP 相关的进程。

phpMyAdmin 安全性

phpMyAdmin 在 XAMPP 和 EasyPHP 中的初始安装有用户名 root ,没有密码。如果您在自己的计算机上使用这些设置,则在连接到互联网时会有安全风险。如果您创建数据库不仅仅是为了个人使用或自己的教育,那么您应该创建一个 phpMyAdmin 密码。如果您与其他人在同一个房间工作,如果密码不泄露给其他人,密码将提供安全性。作为最佳实践,您应该用密码保护您的开发环境。

如果你还没有进入 phpMyAdmin,请进入 XAMPP 控制面板或 easyPHP DevServer 页面。在 XAMPP,点击 MySQL 右边的管理。在 EasyPHP 中,点击页面上显示的 phpMyAdmin 版本右边的打开按钮(图 1-8 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-8

phpMyAdmin 页面

请注意,如果您已经更改了默认端口,您可能需要在启动 phpMyAdmin 时添加端口号。例如,如果您将端口更改为 8080,phpMyAdmin 的地址现在如下所示:

http://localhost:8080/phpMyAdmin/

点击 phpMyAdmin 中的用户账户选项卡(图 1-9 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-9

phpMyAdmin 用户帐户选项卡

单击最后一个条目 root localhost 右侧的编辑权限链接。将显示编辑权限页面(图 1-10 )。单击页面顶部的更改密码链接。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-10

phpMyAdmin 编辑权限页面

在两个框中输入您的密码。单击页面底部附近的“确定”按钮。如果密码更改,您将收到密码已更改的通知。您现在需要告诉 phpMyAdmin 使用新密码。

在 XAMPP,进入你电脑上的 XAMPP 文件夹。打开文件夹 phpMyAdmin。现在在微软记事本(或其他文本编辑器)中打开文件config.inc.php

在 easyPHP 中,进入 EasyPHP-DevServer-xx 文件夹,进入 eds-modules ,打开 phyMyAdmin 文件夹。现在在微软记事本(或其他文本编辑器)中打开文件config.inc.php

搜索以下内容:

['auth_type'] = 'config';

将该行更改为:

['auth_type'] = 'cookie';

保存文件。

如果您使用的是 XAMPP,请返回控制面板。停止并重启 MySQL。返回 phpMyAdmin 页面(http://localhost/phpMyAdmin/http://localhost:8080/phpMyAdmin/)。该页面现在应该要求您输入用户 ID (root)和密码(您刚刚创建的那个)。

如果您使用的是 EasyPHP,请尝试返回 DevServer 页面并重启服务器。然后单击 phpMyAdmin 的 open 按钮。输入用户 ID (root)和密码(您刚刚创建的那个)。如果在输入用户 ID 和密码后出现错误消息,指出 mysql_native_password 丢失,请按照下面的说明操作。

进入 EasyPHP-DevServer-xx 文件夹,进入 eds-binaries ,然后进入 dbserver ,进入 MySQL 文件夹,最后进入 bin 文件夹。该文件夹包含几个可用于运行和管理 MySQL 和 phpMyAdmin 的应用。但是,这些应用只能在命令行界面中运行。为了更容易地找到和使用它们,我们将把这个位置放在 PC 的 Path 变量中。这允许您只执行您需要的程序,而无需输入完整的路径名。转到浏览器中的 URL 行,双击该地址。现在应该突出显示完整的地址。右键单击突出显示的地址,然后选择复制。

转到搜索图标(系统托盘的右下角),点击它,并输入系统变量。单击编辑系统环境变量结果。将出现“系统属性”窗口。单击环境变量按钮。在提供的框中(顶部框代表机器的所有用户,底部框代表您登录时使用的用户 ID),选择路径代码。然后单击编辑按钮。现在单击“新建”按钮。现在将在框中提供一个空间来输入您复制的路径。在框中单击,右键单击,然后选择粘贴(或按 Ctrl+V)。您复制的路径现在应该在框中。确保它是到媒体夹位置的完整路径。正确后,单击“确定”按钮。您的 bin 现在被添加到 PATH 变量中。单击每个窗口的“确定”,关闭窗口并提交变更。现在,您需要关闭所有进程并重启您的机器。这将导致 Windows 使用新路径。

重新启动机器后,重启 EasyPHP DevServer 并确保 HTTP 服务器和 mySQL 服务器都已启动。转到窗口的搜索图标,输入 cmd 。然后按回车键。这将打开命令行窗口。现在,您终于可以将缺少的模块添加到 phpMyAdmin 中了。

输入 mysql -u root 并点击回车键。你现在应该会看到一个 mysql 提示(mysql >)。输入使用 MySQL;(不要忘记分号)并点击回车键。这告诉服务器使用 MySQL 数据库。输入下面一行来添加缺少的插件:

update user set plugin="mysql_native_password" where user="root";

您应该会看到一条消息“Query OK”现在,还可以在命令行中使用以下行来更改密码:

update user set Password=PASSWORD('12345') where user="root";

12345 应该替换为您之前设置的密码。您还应该看到一条消息“Query OK”您的所有更改应该都已完成。通过输入 exit 关闭 mysql 提示符。按 X 键关闭命令窗口。返回 phpMyAdmin 并单击用户帐户。您现在应该看到 Yes 标志已经设置,表明 root 现在有了密码。注意,即使设置了密码,EasyPHP 仍然可能自动让您进入 phpMyAdmin。

这种密码情况发生在免费 MySQL edition 的当前版本(截至本书出版之日)上。正如您之前注意到的,这在 XAMPP 不会发生,因为 XAMPP 使用的是 MariaDB。

如果你对这些步骤有任何问题,并想退出,最简单的方法是使用主软件包文件夹下的卸载程序卸载软件包,然后重新安装 XAMPP 或 EasyPHP。如前所述,如果你收到错误信息,将它们复制到网络搜索引擎(Google)中,寻找建议的解决方案。

直接访问 phpMyAdmin

如果您已经安装了 XAMPP 并且服务器已经启动,那么您可以通过在任何浏览器中输入以下 URL 来直接访问 phpMyAdmin:

http://localhost/phpMyAdmin/http://127.0.0.1/phpMyAdmin/

(如果您更改了默认端口 80,请记住包括最后的正斜杠和端口号。)

如果您已经安装了 easyPHP 并且服务器已经启动,您可以通过在任何浏览器中输入以下 URL 之一来直接访问 phpMyAdmin:

http://localhost:8080/eds-modules/phpmyadmin470x170718155219/
http://127.0.0.1:8080/eds-modules/phpmyadmin470x170718155219/

(您的实际 phpmyadmin 版本/构建可能不同,这将创建一个不同的 phyMyAdmin 文件夹。)

一定要包括http://;否则,浏览器将在互联网上而不是在您的 PC 上查找该位置。

注意

描述 phpMyAdmin 使用的部分适用于任何开发平台:XAMPP、WAMPServer 或 easyPHP。

您可能已经在 XAMPP 或 EasyPHP 中设置了密码,所以无论何时访问 phpMyAdmin,您可能都需要使用该密码登录。这样可以防止互联网机器人和人类干扰你的数据库。如果你和别人一起在办公室工作,后一种情况很重要——你工作的地方可能会有间谍或爱管闲事的人。如果为 phpMyAdmin 创建了密码,会出现一个对话框,如图 1-11 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-11

在对话框中输入密码以访问 phpMyAdmin

输入您的用户名(root)和密码,然后单击 Go 按钮。phpMyAdmin 加载相当慢,但它最终会出现。

请注意,开源程序在不断改进和升级,您可能会发现您的 XAMPP 或 EasyPHP 包中有比本书中使用的版本更新的 phpMyAdmin 版本。您可能还会在 phpMyAdmin 主窗口中看到升级消息,提醒您有新版本。就个人数据而言,安全性是最重要的,所以这些增量更新对你来说是一件好事,尽管它们确实意味着书中的一些截图可能不再准确反映你在屏幕上看到的内容。如果一个界面看起来与本书中显示的有些不同,不要担心;用法通常是相似的。

phpMyAdmin 接口起初看起来可能有点令人畏惧,但是当我们需要使用它们时,我们将介绍它的相关部分。现在,您可以关闭 phpMyAdmin 窗口。

您现在已经知道了如何安装和保护 XAMPP 和 easyPHP,还学习了如何启动和停止服务器。你刚刚读到的大部分内容可能非常新,但有一些部分你会认识,因为它们遵循正常的 Microsoft Windows 文件和文件夹组织。

熟悉的片段

在 XAMPP 软件包中,文件夹和文件的结构将为 Windows 用户所熟悉,尽管它们的名称可能无法识别。

图 1-12 显示了 XAMPP 文件夹。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-12

XAMPP 包中的文件夹;您的列表可能略有不同

在图 1-12 中,注意 htdocs 文件夹。这是你放置你的网站和数据库的所有 PHP 文件和 HTML 页面的地方。

在 XAMPP 文件夹中,你会发现一个名为 MySQL 的文件夹。这个文件夹包含一个名为数据的文件夹,数据库和表格将存放在这里。数据库必须有唯一的名称。数据文件夹中的一个文件包含关于数据库的所有信息,其文件类型为 *。选择

表格是文件;当你创建了任何表格后,这些表格也将存在于名为 data 的文件夹中,它们的文件类型为 *。frm

在 easyPHP 中,一旦你打开 EasyPHP - Devserver-xx 文件夹,你会发现一个 eds-www 文件夹。这是放置所有 PHP 文件和 HTML 页面的地方。以下文件路径是数据库文件的位置:

C:\ easy PHP-Devserver-17 \ eds-binaries \ dbserver \ MySQL 5717 x86x 170717151041 \ data

中级或高级开发人员应该考虑创建虚拟目录,这是可执行文件的其他位置。虽然本书没有涉及这个主题,但是你可以通过搜索 XAMPP 虚拟目录easyPHP 虚拟目录在互联网上找到更多信息。

现在,您已经熟悉了将要使用的工具的外观和感觉,您已经准备好继续前进了。下一节将带您接近创建您的第一个数据库和表。

规划数据库:重要的第一步

第一步也是最重要的一步是规划数据库,这样你就有实际的东西可以用了。让我们假设我们需要为一个组织的成员计划一个数据库。

首先,决定数据库的名称。我们将这个数据库命名为 simpledb 。请记住,数据库就像一个空文件夹,最终将包含一个或多个表。

然后,将数据项组合成一个表。我们将该表命名为用户

接下来,决定你想要表格中的什么信息;您的决定没有约束力,因为您可以在开发过程中更改数据库的任何部分。假设我们需要关于用户的五条信息。我们在本章前面的表 1-1 和表 1-2 中列出了一些典型数据。

表 1-2

simpledb 数据库中用户表的计划草案

|

用户 id

|

名字

|

姓氏

|

电子邮件

|

密码

|

注册日期

|
| — | — | — | — | — | — |
|   | 凯文 | 水壶 | kev@kettle.co.uk | k3 TTL 2fur |   |
|   | 苏珊 | 炖锅 | sue@kitchen.org.uk | n @ its5 |   |
|   | 奥利弗 | 烤箱 | oliver@cooker.co.uk | H0tsts0v3 |   |

表格中的每一行称为一条记录,每一个单元格称为一个字段。一个数据库可以包含多个表。我们使用了一些虚构的名字来帮助设计这张桌子。第一列标记为 user_id,这一列是对五列数据的补充。稍后将解释列 user _ id 暂时接受它,并确保它是空的。此外,将注册日期留空,因为这是自动输入;它不需要输入值,也不需要分配空间。

现在我们必须为数据分配一些空间。表 1-3 显示了我们为每个项目分配的字符数。

表 1-3

用户表中列允许的字符数

|

用户 id

|

名字

|

姓氏

|

电子邮件

|

密码

|

注册日期

|
| — | — | — | — | — | — |
| six | Thirty | Forty | Fifty | Sixty |   |

写下或打印这两个表格,并把它们放在手边,因为在接下来的阶段你会用到它们。

现在,决定数据库的用户名和密码,并将该信息输入到您的笔记本中。需要四条信息:数据库名称、主机、密码和用户名。在本项目中,这些因素如下:

  • 名称 : simpledb

  • 主机:本地主机

  • 密码 : Hmsv1ct0ry1(这来自 hmsvictory,但是第一个字母大写,并且将 I 替换为 1,将 o 替换为 0,作为强密码)

  • 用户 : horatio

为了生成更安全的密码,phpMyAdmin 包含了一个密码生成器。

接下来,我们将使用 phpMyAdmin 创建我们的第一个数据库。

使用 phpMyAdmin 创建数据库

直接打开 phpMyAdmin,如本节开头所述,或者通过 XAMPP 管理页面或 easyPHP 仪表板。请记住,数据库服务器必须正在运行才能使用 phpMyAdmin。

单击顶部菜单中的数据库选项卡。然后你会看到如图 1-13 所示的界面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-13

用于创建数据库的 phpMyAdmin 接口

键入数据库的名称。对于本例,它将是 simpledb,全部小写。您应该将排序规则更改为 utf8-general-ci;latin1_swedish_ci 的默认值会引起混淆。

注意

排序规则确定字符类型、排序能力、性能和数据的其他特征。排序规则 utf8-general-ci 是一个较旧的标准,可以满足本书中数据示例的需要。您的数据要求可能需要不同的排序规则。

然后单击 Create 按钮(将 Collation 字段设置为 utf8-general-ci)。单击“创建”按钮后,页面将切换到“创建表”页面。然而,首先我们想改变数据库的属性。检查页面顶部(左侧)的左箭头。这将返回到主页。再次单击数据库选项卡。现在,上一页看起来类似于图 1-14 。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-14

在页面的下半部分,选择数据库名称旁边的框

如图 1-14 所示,当您选择新数据库旁边的框时,单击 Check Privileges,您将被带到一个屏幕,在那里您将看到有权访问该数据库的用户列表。为了保证数据库的安全,您必须添加用户名和密码。点击添加用户账号,如图 1-15 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-15

添加用户帐户图标位于该图的底部

点击添加用户帐户将加载添加用户帐户屏幕,如图 1-16 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-16

此屏幕允许您添加用户和密码

警告

添加用户名和密码是必不可少的;否则,您的数据库将是不安全的,容易受到不道德的个人或他们的机器人的攻击。这是最需要培养的习惯。请务必在笔记本上记录用户和密码的详细信息。请确保创建一个包含大小写字母、数字和特殊字符的强密码。做一个详细的记录会让你以后少受几个小时的挫折。

使用下拉菜单,接受第一个字段中的默认使用文本字段,并在它右侧的字段中输入用户名。在标记为主机的第二个字段中,选择本地。单词 localhost 将出现在右边的字段中。Localhost 是您计算机上服务器的默认名称。在第三个字段中输入密码,并在下方的字段中再次输入密码进行确认。如果你想要一些独特和更安全的东西,生成密码按钮将创建一个随机的强密码。

向下滚动到用户帐户的数据库,然后单击授予数据库的所有权限。因为你是站长,你需要能够处理数据库的方方面面;因此,你需要所有的特权。如果添加其他用户,您需要通过取消选择 Drop、Delete 和 Shutdown 等框来限制他们的权限。

向下滚动到表单的底部,然后单击“确定”或“执行”按钮。现在,您已经创建了数据库并保护它免受攻击。数据库可以被视为一个空文件夹,最终将包含一个或多个表。

注意

如果你在使用 phpMyAdmin 的时候迷路了,看不到下一步该做什么,总是点击左边面板顶部的小房子。将鼠标悬停在图标上,以确保它是主屏幕按钮。

现在我们将创建我们的第一个表。

使用 phpMyAdmin 创建一个表

现在让我们使用 phpMyAdmin web 页面将一个或多个数据表插入到数据库中。此页面将使您能够完全控制您的表,包括故障排除和备份。

单击新数据库的名称;你会在左边的面板找到它。然后你会看到如图 1-17 所示的屏幕。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-17

单击页面底部的“Go”按钮创建表格

输入表格的名称,并指定列数。然后单击页面底部的“Go”按钮。你将被带到一个屏幕,显示列翻转 90 度,使列看起来像行;如图 1-18 所示。字段为空,等待您定义表格。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-18

六行代表六列。列标题将输入左侧的字段中。

使用我们之前计划的表 1-2 和 1-3 中的数据,并输入列名、数据类型和字符数。创建用户表的细节在表 1-4 中给出。

表 1-4

用户表的属性

|

列名

|

类型

|

长度/值

|

默认

|

属性

|

|

索引

|

A_I

|
| — | — | — | — | — | — | — | — |
| 用户 id | 介质 | six | 没有人 | 无符号 | -是吗 | 主要的 | ·······················。 |
| 名字 | 可变长字符串 | Thirty | 没有人 |   | -是吗 |   | -是吗 |
| 姓氏 | 可变长字符串 | Forty | 没有人 |   | -是吗 |   | -是吗 |
| 电子邮件 | 可变长字符串 | Fifty | 没有人 |   | -是吗 |   | -是吗 |
| 密码 | 茶 | Sixty | 没有人 |   | -是吗 |   | -是吗 |
| 注册日期 | DATETIME |   |   |   | -是吗 |   | -是吗 |

除了 user_id 之外,接受每个项目的所有默认设置。在这里,您需要选择 UNSIGNED、PRIMARY 和 type 同时选择 A_I(自动增量)框。如果您发现输入值 6、UNSIGNED 和 PRIMARY 会导致您的 phpMyAdmin 版本产生错误(或警告),那么您可以保留这些项的默认值。如后文所述,当您单击 A_I 时,系统会要求您选择 PRIMARY。MEDIUMINT 将默认为 6 号。

标题类型下的各种类别将在后面解释;标题长度/值指的是最大字符数。registration_date 的长度/值留空,因为长度是预先确定的。不要在标题 Default 和 NULL 下输入任何内容。属性 UNSIGNED 意味着 user_id 整数不能是负数。user_id 的索引是主索引,A_I 表示 id 号的自动递增;当每个用户注册到数据库中时,他们被赋予一个唯一的号码。每增加一个新用户,该数字就增加一。当选择 A_I 复选框时,一个弹出窗口可能会要求您验证它是否是主索引,并再次请求大小。不要在弹出窗口中输入尺寸。如果你这样做,你会得到一个错误。图 1-19 显示了指定属性的屏幕。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-19

此屏幕允许您指定列标题和内容类型

行代表列,它们非常宽;您可能需要水平滚动来输入一些信息。当你向右滚动时,你会发现更多的选项,但是在本教程中我们不需要它们。

你如何填写字段?在标题名称下左侧的字段中输入六个列标题。在标题类型下的第二列字段中选择列的类型。从下拉菜单中选择它们。此表中使用的类型如下:

  • MEDIUMINT 可以存储从负 8,388,608 到正 8,388,607 的整数。如果用户数量永远不会超过 65,535,您可以选择下一个最小的类别 SMALLINT。

  • VARCHAR 指定长度从 0 到 255 的可变字符串。

  • CHAR 是传统上用于密码的字符串。确保这个长度为 60 个字符,以便您的 PHP 代码可以散列密码。当前的 PHP 哈希算法会将密码转换为 60 个字符的哈希字符串。比如说,用户的密码可以是 6 到 12 个字符长,但是它仍然会作为一个散列的 60 个字符的字符串存储在数据库中。这将在第二章中进一步讨论。

    注意在 PHP 的新版本中,散列密码的长度可能会增加。在设置表中该字段的长度之前,请确保研究当前的哈希大小。

  • DATETIME 以 YYYY-MM-DD-HH:MM:SS 格式存储日期和时间。

在标题长度/值下的第三列字段中输入字符数。这些数字请参考表 1-4 。

在标题 Default 下,接受缺省值 None。如果需要,此字段允许您输入默认值。

您可能需要向右滚动以完成字段的填写。在标题“属性”下,使用下拉菜单为 user_id 选择“无符号”。这确保了整数范围从 0 到 16,777,215。负数量不适用于 user_id。

接下来的两个条目只涉及 user_id(图 1-20 )。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-20

user_id 列的两个额外条目

对于 user_id,在标题 Index 下,单击下拉菜单选择 PRIMARY。user_id 应该始终是主索引。

在标题 A_I 下,选择最上面的复选框,这样当每个新记录添加到数据库中时,user_id 号会自动递增。如前所述,您可能会看到一个弹出窗口,要求您验证它是主要的和大小。不要在弹出窗口中输入尺寸。验证和/或添加任何缺失的信息,然后单击确定。

输入表格和图 1-20 中显示的附加信息。完成信息输入后,滚动到底部并单击保存按钮。

警告

如果您忘记为 user_id 选择 A_I 框,当您稍后尝试输入第二条记录时,您将收到一条错误消息。该消息将说明您正在尝试为 user_id 创建一个重复值 0。

有些人喜欢混合使用 GUI 和命令行来对表格进行编程;phpMyAdmin 允许您通过使用 SQL 语言来做到这一点。然而,这本书将主要使用 phpMyAdmin GUI。接下来描述 SQL 替代方法。如果您愿意,可以跳过这一节,但是我们建议您以后再来看这一节,因为您肯定会在其他更高级的书籍和教程中遇到 SQL。

SQL 替代方案

下一节描述了使用 phpMyAdmin 创建数据库和表的一种稍微快捷的方法。SQL 代表结构化查询语言;它是 MySQL、MariaDB 和许多其他数据库的官方语言。你会很高兴看到它使用简单的英语命令。唯一的问题是,在 SQL 窗口中比在图 1-19 中显示的 phpMyAdmin 接口中更容易产生打字错误或拼写错误。

使用 SQL,可以创建一个带有密码和用户名的数据库。这样就省了好几个步骤。

我们假设您已经创建了数据库 simpledb,所以我们不能再使用这个名称。假设管理员(Adrian)想要使用以下信息创建一个名为 members 的数据库:

  • 数据库名称:成员

  • 特权:全部

  • 用户名:阿德里安

  • 密码:订书机 12

图 1-21 显示了在 SQL 窗口中创建数据库的代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-21

SQL 窗口

在 phpMyAdmin 中,在窗口左侧面板的数据库“树”上单击 New,通知 phpMyAdmin 您不再使用 simpleDB 数据库。单击 SQL 选项卡(显示为圆圈)以显示 SQL 窗口。

图 1-21 中显示的细节必须按以下格式输入:

CREATE DATABASE members;
GRANT ALL
ON members.*
TO 'adrian'
IDENTIFIED BY 'stapler12';

通过在每行末尾按 Enter 键,可以在单独的行上输入每一项。SQL 关键字(如 CREATE DATABASE)传统上是大写的。其他项目通常以小写字母输入。注意分号和单引号——它们很重要。当您对条目感到满意时,单击 Go 按钮。

如果输入的代码正确,将显示两条查询语句,表明数据库已创建,adrian 作为用户被附加。它们将指示空的结果集。这是可以的,因为数据库中不存在任何东西。

现在,我们将使用 SQL 窗口在成员数据库中创建一个名为 users 的表。

单击 phpMyAdmin 左侧面板上的数据库“树”中的成员数据库。如果成员数据库没有出现,请刷新页面使其出现。打开 SQL 窗口,输入以下内容:

CREATE TABLE users (
user_id MEDIUMINT (6) UNSIGNED
AUTO_INCREMENT,
first_name VARCHAR(30) NOT NULL,
last_name VARCHAR(40) NOT NULL,
email VARCHAR(50) NOT NULL,
password CHAR(60) NOT NULL,
registration_date DATETIME,
PRIMARY KEY (user_id)
);

前面代码中显示的 NOT NULL 将要求在字段中输入数据。这是必填字段。在本例中,所有字段实际上都是必需的,因为 registration_date 是自动填充的。

图 1-22 显示了 SQL 窗口中的详细信息。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-22

在 phpMyAdmin 的 SQL 窗口中创建一个表

注意括号都是普通括号,不是花括号。在每一行之后按 Enter 键,并且记住在最后一行的末尾加上右括号和分号。每一项都用逗号分隔(第 3 行到第 8 行);如果你的表格有六列,你应该有六个逗号。单击 Go 按钮,表将被创建。

小费

我们鼓励您探索刚刚描述的 SQL 主题。在未来的某个时候,使用 SQL 的能力将是一个有用的选择。你可以通过搜索互联网找到许多免费教程。

删除数据库和表

在学习时,初学者通常需要在创建数据库或表后重新开始,并可能希望删除以前的尝试。当您第一次使用 phpMyAdmin 时,您可能会忘乎所以,创建几个数据库和表。然后你可能会决定清理这些乱七八糟的东西,删除一些。

让我们看看如何删除数据库。如果 phpMyAdmin 尚未打开,请使用前面显示的方法之一启动它。选择数据库选项卡(如图 1-23 所示),然后选择要删除的数据库旁边的框。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-23

删除数据库

选择要删除的数据库后,单击 Drop 图标(在右下角)。将询问您是否确实要删除该数据库;继续并完成删除。与该数据库相关的所有内容都将被删除,包括它的表。如果成功,会有一条消息告诉您一个数据库已被删除。

您可能希望保留一个数据库,但删除它的全部或一个表。在 phpMyAdmin 中,在左侧面板的“树”中选择数据库。在下一个屏幕中,您将看到表格;选择要删除的表旁边的框。图 1-24 显示了 simpledb 数据库的用户表。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 1-24

在 phpMyAdmin 中删除表

选择要删除的表后,单击拖放图标(位于表名右侧)。系统会询问您是否真的要删除这些表。您可以在删除和取消之间进行选择。

摘要

在这一章中,我们定义了一个数据库,然后看了两个用于开发和测试数据库和 PHP 文件的免费平台。我们希望你成功下载并安装了 XAMPP 或 easyPHP。然后,我们研究了 phpMyAdmin,并学习了如何使用它来创建数据库和表。SQL 被研究作为创建数据库和表的替代方法。我们学习了如何使用用户 ID 和密码来保护 phpMyAdmin 和数据库。我们发现了如何使用 phpMyAdmin 删除数据库和表。在下一章,我们将创建并测试简单的交互式网页。

二、创建与用户互动的网页

本章将演示如何将一个简单的数据库链接到一个网页上,这样网页就可以与用户交互。公众可以访问网站,但不能访问数据库结构或表格中的数据。他们将不被允许查看受保护的页面,例如会员专用页面,因为他们在注册之前没有密码。但是,网站设计者必须为用户提供一种与数据库交互的方式,以便注册成为会员、搜索数据库和更改密码。PHP 语言和 SQL 代码将提供解决方案。

完成本章后,您将能够

  • 为网站创建数据库和模板

  • 理解和使用 PHP 包含函数

  • 了解服务器如何处理 PHP 页面

  • 创建交互式模板页面

  • 创建 mysqli 代码来连接数据库

  • 为组织成员创建注册页面

  • 理解并使用 PHP echo()语句

  • 创建粘性表单

  • 使用简单数组

  • 创建显示成员记录的表单

  • 创建哈希密码的代码

对于我们的交互式 web 页面,我们将使用 simpledb 数据库和以前项目中的 users 表。请注意,本教程既不实用也不安全。它是后续章节中描述的更安全、更雄心勃勃的项目的垫脚石。实际上,您永远不会允许普通成员查看成员列表。该项目的互动要素如下:

  • 用户可以通过在屏幕上显示的表格中插入他们的详细信息来注册成为会员。注册细节将被输入到数据库表中,并可由管理员用来发送定期通讯给成员。

  • 注册用户可以更改他们的密码。

  • 用户可以查看成员列表(仅限于此项目)。在后面的章节中,这个功能将只对网站管理员和会员秘书开放。

使这个例子不适合真实世界的特征如下:

  • 没有规定注册成员随后登录进入一个特殊的部分或页面。这将在第三章中讨论。

  • 用户应该永远不能访问成员的详细信息表。

  • 在这个早期阶段,为了简单起见,提供了对用户信息的有限过滤。因此,该表可能包含错误的数据和伪造的电子邮件地址。

  • 仅在本章中,任何知道成员的电子邮件地址和旧密码的用户都可以更改成员的密码。

所有这些安全问题将在后面的章节中讨论。尽管有缺点,该项目将为您提供编码和测试页面的宝贵实践。您还将学习更多的数据库术语和一些基本的 PHP 代码。

创建用于保存数据库页面的文件夹

在 XAMPP 的 htdocs 文件夹或者 easyPHP 的 eds-www 文件夹中,新建一个名为 simpledb 的子文件夹。本章中创建的所有页面都将放在 simpledb 文件夹中。您可以选择从提供的清单中手工编码文件,或者将书的代码加载到 simpledb 文件夹中。(你可以从 Apress 下载代码。com 。)我们建议您手工编写本章的程序;这些文件很小,创建时间不会太长。如果您键入并测试代码,您将会学到更多并学得更快,尤其是当您犯了错误并学会纠正它们时。

创建临时模板

显然,交互式数据库的某些方面必须是用户可以访问的。这意味着将它整合到现实世界的网页中。我们将把我们的网页命名为template.php(如图 2-1 )。正如您所看到的,有一个主标题、一些正文、左侧的导航侧栏、右侧的信息栏和页面底部的页脚。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-1

模板

在清单 2-1 中,head 部分包含 DOCTYPE、页面标题和到 Bootstrap 样式表的链接。页面的主体包含一些 PHP 代码,这将在清单的最后一步一步解释。

因为文件包含 PHP 代码(不管有多少),所以文件以文件类型保存。php

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Template for an interactive web page</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS File  -->
  <link rel="stylesheet"
  href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
  integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4"
  crossorigin="anonymous">
</head>

<body>
<div class="container" style="margin-top:30px">

<!-- Header Section                                                                 #1-->
<header class="jumbotron text-center row"
style="margin-bottom:2px; background:linear-gradient(white, #0073e6);padding:20px;">
  <?php include('header-for-template.php'); ?>
</header>

<!-- Body Section                                                                   #2-->
  <div class="row" style="padding-left: 0px;">
<!-- Left-side Column Menu Section -->
  <nav class="col-sm-2">
      <ul class="nav nav-pills flex-column">
        <?php include('nav.php'); ?>
      </ul>
  </nav>

<!-- Center Column Content Section -->
  <div class="col-sm-8">
     <h2 class="text-center">This is the Home Page</h2>
        <p>The home page content. The home page content. The home page content. The home page content. <br>
        The home page content. The home page content. The home page content. The home page content. <br>
        The home page content. The home page content. <br>
        The home page content. The home page content. The home page content. </p>
  </div>

<!-- Right-side Column Content Section                                              #3-->
   <aside class="col-sm-2">
       <php include('info-col.php'); ?>
   </aside>
 </div>

<!-- Footer Content Section                                                         #4-->
<footer class="jumbotron text-center row"
style="padding-bottom:1px; padding-top:8px;">
  <?php include('footer.php'); ?>
</footer>
</div>
</body>
</html>

Listing 2-1Creating a Template for the Project (template.php)

注意

标签让服务器知道 PHP 代码在哪里结束。服务器会将这些标签之间的任何代码传递给 PHP 解释器来执行。标签之外的代码,HTML 和 JavaScript 代码,最终将被发送到用户的浏览器,不做任何处理。

清单 2-1 中显示的 PHP 代码包含 include()函数,现在将对其进行解释。(严格来说,include()函数是一个 PHP 语言构造,但区别很小,为了简单起见,我们将继续称它为函数。)

PHP include()函数简介

你会注意到清单 2-1 中似乎没有足够的代码来创建图 2-1 中显示的页面。原因如下。

在清单 2-1 中的标记之间显示的四段 PHP 代码都将一个额外的文件拖入页面。该页面由五个文件组成:一个主文件和四个外部文件。使用 PHP include()函数将四个外部文件拉入模板页面。

对于更新和维护网站来说,include()函数是一个非常好的省时工具。假设您有一个 40 页的客户端网站,每个页面都需要相同的菜单按钮块。如果您的客户要求您添加或删除一个菜单按钮,通常您将不得不修改 40 个页面来添加或删除每个页面上的按钮。使用 include()函数,您可以设计网站,使每个页面都包含这一行 PHP 代码:。这将把菜单按钮块拉进每个网页。你可以在另一个名为menu.php的文件中设计按钮块。要向所有 40 个页面添加一个新按钮,只需将新按钮添加到名为menu.php的文件中。

注意

PHP 函数是一个微小的程序,它将执行特定的任务。include()函数获取括号中的内容,并将其放入页面中 include()函数所在的位置。PHP 有两个类似的函数:include()和 require()。它们都将文件拖入页面,以便该文件包含在显示的页面中。区别在于他们对丢失或有问题的文件的反应方式。如果要包含的文件丢失或损坏,include()不会暂停页面的执行。将出现警告,但页面将继续加载。相反,如果 require()找不到文件或者文件有问题,就会出现错误。在这种情况下,页面将停止执行。使用 include()来处理任何对网页正常处理来说不是绝对必要的内容。在这个例子中,没有合适的标题,页面仍然可以运行。但是使用 require()来加载数据库的详细信息,因为如果数据库无法打开,这个页面就没什么用了。此外,PHP 还有 include_once()和 require_once()函数。这两个函数将确定该文件之前是否已经被包含。如果文件之前已经被包含,include_once()将不会执行,没有抱怨。require_once()不会再次加载已经加载过的文件。

要放入页面的四个元素是标题、菜单按钮块、右侧的信息面板和页脚。包含的文件可以是任何类型——例如,一个. txt 文件,一个*。php* 文件,或者一个*。html* 文件。请注意,您选择的文件类型会影响文件中的代码是否可以从外部查看。HTML 文件可以很容易地在浏览器中查看。但是,不要依赖文件结尾的类型来判断文件是否应该受到保护。确保您的代码位于安全的文件夹中。include()语句可以放在 HTML 页面中的任何地方,只要它被 PHP 标记包围;这些以标签<开头?php 并以标签结束?>。下一节将详细介绍四个包含的外部文件。

你可能也想知道为什么我们不使用内容管理系统(CMS ),比如 WordPress,来组装和显示我们的页面。首先,这不是一本 CMS 书,其次,你实际上是在学习 CMS 系统是如何工作的。它们以相似的方式组合页面。一些 CMS 系统是用 PHP 创建的。

包含的头文件

图 2-2 显示了包含的头文件。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-2

模板的标题

这个标题是临时的。当我们创建模板的交互式版本时,新的标题将包含一个附加菜单,以便用户可以注册,从而将他们的详细信息插入 simpledb 数据库。

警告

现在还不要试图创建四个非交互式页面(page-2.php、【page-3.php、page-4.phppage-5.php),因为在本章的后面,这些页面将需要一个新版本的页眉。

<!-- Header Section                                                                 #1-->
<header class="jumbotron text-center row"
   style="margin-bottom:2px; background:linear-gradient(white, #0073e6); padding:20px;">
   <?php include('header-for-template.php'); ?>
</header>

Listing 2-2aHeader Section of Template File (template.php)

清单 2-2a 中的 PHP 语句将 header-for-template 文件的内容导入页面(代码如清单 2-2b 所示)。此外,引导 CSS 代码被导入到模板文件的 head 部分(参见清单 2-1 的顶部)。使用 Bootstrap,我们可以减少开发的 CSS 数量,并自动创建适应多种屏幕尺寸(包括智能手机)的页面。Bootstrap jumbotron 类产生一个大盒子,用于显示重要信息(如标题)。我们还将文本居中,并将标题声明为一行。引导行由网格控制。每一行都被分成单元格,以帮助页面布局。这类似于电子表格中的单元格和行。我们将确定清单 2-2b 中标题的每一列使用的单元格数量。我们对大屏幕的默认设置做了一些调整,减少了下边距(2px),将背景更改为渐变(白色到蓝色),并更改了填充(20px)。清单 2-2b 包含了临时头的代码。

<div class="col-sm-2">
<img class="img-fluid float-left" src="logo.jpg" alt="Logo">
</div>
<div class="col-sm-8">
 <h1 class="font-bold">Header Goes Here</h1>
</div>

Listing 2-2bCode for the Temporary Header (header-for-template.php)

在清单 2-2a 中,header 标签中引用了 class 行。这允许我们用列来设计引导行的样式。在清单 2-2b 中。我们创建两列:一列保存徽标,另一列保存标题文本。徽标列使用两个小单元格(col-sm-2),标题文本使用八个小单元格(col-sm-8)。img 标签还包括一个 img-fluid 类,以及 float-left 类,它允许引导代码在较小的设备上调整图像的大小。标题文本也被加粗(字体加粗)。

这本书假设你有一些 CSS 和 Bootstrap 编程知识。我们将在每章和附录中提供一些链接,以提供额外的资源。然而,一个很好的学习方法是调整现有的代码,看看它如何影响布局。您可能需要花几分钟时间来调整这个示例和下面的示例,看看它们将如何改变 web 页面的布局。

注意

我们选择使用 Bootstrap 来减少我们必须演示的 CSS 代码量。Bootstrap 是一个开源工具包,包括 HTML、CSS 和 JavaScript。这个工具包提供了一个响应式网格系统和预构建的 CSS/JS 组件。使用 Bootstrap,开发人员可以在 HTML、CSS 和 JavaScript 编码上花费更少的时间,而在编程(使用 PHP)上花费更多的时间。

在本书中,我们将使用 Bootstrap 类来提供网页的多平台格式。工具包可以直接在线链接(如我们在示例中所做的那样)或从 Bootstrap 网站下载。您可以使用 CSS 调整许多工具包组件的默认设置。如果您不熟悉 Bootstrap,我们建议您查看并参考以下站点以了解更多信息:

www.getboostrap.com

https://www.w3schools.com/bootstrap/bootstrap_get_started.asp

包含的菜单文件

图 2-3 显示了包含的菜单按钮块。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-3

包含的菜单按钮

清单 2-3a 给出了模板页面中菜单的 HTML 代码。

<!-- Body Section                                                                      #2-->
  <div class="row" style="padding-left: 0px;">
<!-- Left-side Column Menu Section -->
  <nav class="col-sm-2">
      <ul class="nav nav-pills flex-column">
       <?php include('nav.php'); ?>
      </ul>
  </nav>

Listing 2-3aNavigation Menu of Template File (template.php)

与页眉类似,页面的主体部分定义了一行。一行有三列。左栏(col-sm-2)为导航菜单保留了两个单元格。菜单将以“药丸”格式显示(见图 2-3 ),并且对于不同的设备尺寸是灵活的。一条 PHP 语句导入 nav.php 的文件,该文件包含链接的实际代码清单(参见清单 2-3b )。

  <li class="nav-item">
          <a class="nav-link active" href="index.php">Home</a>
  </li>
  <li class="nav-item">
          <a class="nav-link" href="page-2.php">Page 2</a>
   <li>
   <li class="nav-item">
          <a class="nav-link" href="page-3.php">Page 3</a>
   </li>
   <li class="nav-item">
          <a class="nav-link" href="page-4.php">Page 4</a>
   </li>
   <li class="nav-item">
          <a class="nav-link" href="page-5.php">Page 5</a>
     </li>

Listing 2-3bThe Navigation Menu (nav.php)

导航部分(菜单)的代码包括一个无序列表,该列表中有指向网站每个未来页面的链接。这是用无序列表创建菜单的常见做法。每个列表项都定义了一个类 nav-item,Bootstrap 使用它来格式化菜单。每个链接( 2-3 )。还有一个使菜单项变灰的 deactive 类。

包含的信息列

包含信息栏位于页面右侧,如图 2-4 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-4

模板中包含的信息列

清单 2-4a 给出了模板文件中信息列的 HTML 代码。

<!-- Right-side Column Content Section                                                 #3-->
   <aside class="col-sm-2">
         <php include('info-col.php'); ?>
   </aside>

Listing 2-4aInformation Column in Template File (template.php)

信息栏保留两个小单元格用于显示(col-sm-2)。PHP include 语句从info-col.php获取信息列的细节(参见清单 2-4b )。

<div>
<h3>This is the information column</h3>
        <p>Web design by <br>A W West and<br>Steve Prettyman</p>
</div>

Listing 2-4bThe Information Column Details (info-col.css)

清单 2-4b 包括在页面右侧显示信息的 HTML 代码。信息移动到右侧,因为在定义信息列之前,菜单和页面的主要部分(稍后讨论)首先用列和单元格定义。

包含的页脚文件

图 2-5 显示了包含的页脚。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-5

模板中包含的页脚

清单 2-5a 给出了页脚的 HTML 代码。

<!-- Footer Content Section                                                            #4-->
<footer class="jumbotron text-center row"
style="padding-bottom:1px; padding-top:8px;">
  <?php include('footer.php'); ?>
</footer>

Listing 2-5aFooter code for Template File (.php)

页脚也被声明为一个超大屏幕和一行。文本居中(文本居中)。此外,超大屏幕的默认设置被调整为减小块大小,以更适合页脚。PHP 代码获取要显示的 HTML(参见清单 2-5b )。

<p class="h6 col-sm-12">Copyright &copy; Adrian West & Steve Prettyman 2018  Designed by
<a href="http://www.colycomputerhelp.co.uk/">Adrian West </a> and
<a href=http://www.littleoceanwaves.com>Steve Prettyman </a>  Valid
<a href="http://jigsaw.w3.org/css-validator/">CSS</a> &amp;
<a href="http://validator.w3.org/">HTML5</a></p>

Listing 2-5bThe Footer (footer.php)

清单 2-5b 使用 Bootstrap 类 h6 来确定用于显示文本的字体属性。段落标记还保留了 12 个小列(页面的完整宽度)来显示页脚信息。

服务器如何处理页面?

当 HTML 文件保存为 PHP 文件时,。php 扩展提醒服务器像平常一样处理 HTML,但是也寻找任何 php 代码。默认情况下,服务器处于 HTML(复制)模式,任何 HTML 代码都照常发送到浏览器。它什么时候找到了<?php 标签,服务器切换到 PHP(解释)模式。任何 PHP 代码都被解释。执行解释的 PHP 代码的结果被返回到页面,而不是实际的 PHP 代码本身。它继续以这种模式运行,直到遇到 PHP 结束标记?>;然后切换回 HTML(复制)模式。这种循环行为一直持续到代码页的末尾。

模板的交互式版本

在这个版本的模板中,我们将通过创建一个带有附加菜单的新头文件来引入交互性。该菜单将允许用户注册并将他们的详细信息输入 simpledb 数据库。它还允许用户更改他们的密码和查看成员表。

图 2-6 显示了新的割台。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-6

标题中会添加一个注册菜单

交互式元素将嵌入标题中;因此,前面的标题现在被修改为包含一个菜单。新的表头将被命名为header.php,代码如清单 2-6 所示。

<div class="col-sm-2">
<img class="img-fluid float-left" src="logo.jpg" alt="Logo">
</div>
<div class="col-sm-8">
 <h1 class="blue-text mb-4 font-bold">Header Goes Here</h1>
 </div>
     <nav class="col-sm-2">
         <div class="btn-group-vertical btn-group-sm" role="group" aria-label="Button Group">
  <button type="button" class="btn btn-secondary" onclick="location.href = 'register-page.php'" >
                   Register</button>
  <button type="button" class="btn btn-secondary" onclick="location.href = 'register-view_users-page.php'">
                  View Users</button>
<button type="button" class="btn btn-secondary" onclick="location.href = 'register-password.php'" >
                  New Password</button>
</div>
    </nav>

Listing 2-6Placing a Registration Menu in the New Header (header.php)

在本例中,创建了一个引导垂直按钮组(btn-group-vertical ),其中包含一些小按钮(btn-group-sm)。该组中的每个按钮都用 HTML 按钮标签和 Bootstrap btn 和 btn-secondary 类来定义。点击属性用于调用用户请求的页面。在后面的章节中,我们还会看到使用引导导航条在标题下创建水平按钮栏的例子。

在模板文件中,我们需要用新的头替换以前包含的头。新的模板页面将被命名为index.php,它现在有两块菜单按钮,如图 2-7 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-7

带有两个菜单的新主页模板

旧模板和新索引文件之间代码的唯一区别是新的头引用。清单 2-7 中的代码片段显示了这些变化。完整的代码包含在 index.php 的文件中,可以从网站下载。com 网站。

<!-- Header Section -->
<header class="jumbotron text-center row"
    style="margin-bottom:2px; background:linear-gradient(white, #0073e6); padding:20px;">
    <?php include('header-for-template.php'); ?>
</header>

Listing 2-7Including the New Header in the Home Page (index.php)

现在你可以创建四个普通的网页,使用新的模板(index.php);文件保存四份,分别命名为 page-2.php*、**、。稍微改变一下这些页面的内容,使它们不同于index.php,并指出用户正在查看的页面。您可能会考虑将活动的导航项目(按钮)更改为当前正在查看的页面。前面显示的代码将主页导航按钮设置为活动状态。*

连接到数据库

在我们可以在数据库中做任何事情之前,我们必须连接到它。这是通过创建一个名为 mysqli_connect.php 的连接文件来实现的。下一个代码片段给出了连接文件的代码。

连接到数据库的代码片段清单(mysqli_connect.php)

<?php
// This file provides the information for accessing the database.and connecting to MySQL.
// First, we define the constants:                                                     #1
Define ('DB_USER', 'horatio'); // or whatever userid you created
Define ('DB_PASSWORD', 'Hmsv1ct0ry'); // or whatever password you created
Define ('DB_HOST', 'localhost');
Define ('DB_NAME', 'simpledb');

// Next we assign the database connection to a variable that we will call $dbcon:      #2
try
{
      $dbcon = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
      mysqli_set_charset($dbcon, 'utf8'); //                                           #4
      // more code will go here later
}
catch(Exception $e) // We finally handle any problems here                             #3
{
     // print "An Exception occurred. Message: " . $e->getMessage();
     print "The system is busy please try later";
}
catch(Error $e)
{
      //print "An Error occurred. Message: " . $e->getMessage();
      print "The system is busy please try again later.";
}
?>

将文件另存为 mysqli_connect.php ,并将其放在 XAMPP htdocs 或 easyPHP eds-www 文件夹中。

注意

当数据库设置在远程主机上时,为了安全起见,该文件被放置在根文件夹的上一级。不保护该文件会导致 DOS 攻击。将该文件放在 web 服务器的公共区域将允许任何人使用该代码访问数据库。将文件放在程序代码的上一层,放在 web 服务器保护的区域中(或另一个安全文件夹中)。使用类似 require('的 require 语句…/MySQL connect . PHP’);访问您的连接信息。本书中的示例从与代码文件相同的位置访问连接文件。这样做是为了便于测试,但不应该在公开可访问的 web 服务器上的真实环境中进行。

在本书中,catch 消息用于调试。实时网站不应该向用户显示错误消息。应该向用户显示一条通用消息(如前面的清单所示)。比起显示错误信息的网站,用户更愿意回到当前“繁忙”的网站。这些错误消息也可能是一个安全漏洞,因为可能会显示可能导致错误的代码行。

代码的解释

mysqli_connect.php 文件包含一些您可能不熟悉的代码约定,所以让我们在这里简单地浏览一下。

单行注释以双正斜杠或哈希符号开头,例如:

// more code will go here later
# more code will go here later

常量是使用关键字DEFINE创建的固定定义。

DEFINE ('DB_USER', 'horatio');

$dbcon这样的变量是用于包含可以改变的信息的记忆存储设备;它们前面有一个美元符号。使用以下格式创建变量: v a r = s o m e i n f o r m a t i o n 。等号叫做 ∗ 赋值运算符 ∗ ;它将等号右边的值赋给左边的变量。该示例将一些信息分配给变量 var = some_information。等号叫做*赋值运算符*;它将等号右边的值赋给左边的变量。该示例将一些信息分配给变量 var=someinformation。等号叫做赋值运算符;它将等号右边的值赋给左边的变量。该示例将一些信息分配给变量var。

我们现在将使用行号作为参考来检查代码。

// First, we define the constants:                                                     #1
Define ('DB_USER', 'horatio'); // or whatever userid you created
Define ('DB_PASSWORD', 'Hmsv1ct0ry'); // or whatever password you created
Define ('DB_HOST', 'localhost');
Define ('DB_NAME', 'simpledb');

作为惯例,而不是规则,程序员创建的常量名称都是大写字母。这有助于识别哪些项是常量(程序运行时不能更改),哪些项是变量(程序运行时可以更改)。在 PHP 中,如果一个项目的第一个字符是美元符号,我们也可以知道它是一个变量。将定义代码行放在程序代码的顶部附近是一个很好的做法。这使得您很容易找到何时必须更改某个值。例如,如果您需要更改税率。

注意

如果没有为 simpledb 数据库创建用户 id 和密码,用户 ID 将是 root,密码将是" "(两个引号,中间没有空格)。

// Next we assign the database connection to a variable that we will call $dbcon:      #2
try
{
      $dbcon = new mysqli(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
      // more code will go here later
}

美元符号后面的文本可以是与存储器中保存的信息相关的任何内容。例如,清单中用于连接数据库的变量名为 d b c o n 。这被选择来表示到数据库的连接;它可以是 dbcon。这被选择来表示到数据库的连接;它可以是 dbcon。这被选择来表示到数据库的连接;它可以是connect_db 或任何其他表示数据库连接的文本。一些程序员喜欢使用 camel case ( d b C o n ) ,其中第一个单词是小写的,后面的任何单词都以大写字母开头。其他人保持所有单词小写,有些人甚至在单词之间使用下划线 ( ) ( dbCon),其中第一个单词是小写的,后面的任何单词都以大写字母开头。其他人保持所有单词小写,有些人甚至在单词之间使用下划线(_)( dbCon),其中第一个单词是小写的,后面的任何单词都以大写字母开头。其他人保持所有单词小写,有些人甚至在单词之间使用下划线()( db _ con)。你使用什么风格并不重要。保持一致,用相同的风格命名变量。

我们使用之前定义的主机、用户名、密码和数据库名称来连接数据库。

try 块(在{ }之间的所有内容都被认为是在一个块中)将把任何问题(错误或异常)都“抛出”到 catch 块中,供程序处理。

catch(Exception $e) // We finally handle any problems here                             #3
{
      //print "An Exception occurred. Message: " . $e->getMessage();
      print "The system is busy please try later";
}
catch(Error $e)
{
      //print "An Error occurred. Message: " . $e->getMessage();
      print "The system is busy please try again later.";
}

出于测试目的,有时我们会显示可能出现的错误和异常消息。在生产中,我们会将打印语句替换为给用户的通用消息,例如“系统当前不可用,请稍后再试。”允许用户看到错误信息会给他们一种感觉,你的网站设计很差,很脆弱,经常会崩溃。在网站上看到错误信息的用户通常会离开网站,并且不再返回。然而,在今天的环境中,常见的“系统当前繁忙或不可用”类型的消息并不罕见。许多网站现在限制用户数量,以防止黑客试图淹没和破坏网站。

让用户看到任何错误消息也是一个主要的安全漏洞。这些消息可能会泄露提供项目实际位置的代码,例如数据库本身。这些信息以及时间戳应该被传递到一个日志文件中,以帮助系统管理员。

您可能会在互联网或较旧的书籍中看到使用或 die 的 PHP 数据库代码的示例代码。从一开始,这就是 PHP 的标准。然而,随着 PHP 7 的发布,设计者鼓励所有开发人员使用行业标准的 try/catch 格式。此外,当使用或 die 时,您无法捕获所有异常和错误。使用 try/catch,您现在可以这样做。在 PHP 7 中,开发人员被鼓励使用 mysqli(而不是 mysql)方法来连接和访问数据库。这被认为是最安全的访问方法。

我们已经将 HTML 编码的语言从默认的改为 utf-8,如清单 2-8 所示。到数据库的连接还必须包括指示正确编码语言的代码。HTML 页面上使用的编码语言类型必须与数据库表中使用的编码语言相匹配。不一致的匹配可能会导致问题,包括无效的搜索结果。

mysqli_set_charset($dbcon, 'utf8');                                                    #4

(请注意,这种格式不同于 HTML 文档中的代码集,因为它不像 utf-8 那样包含连字符。)

接下来,我们需要一些页面来调用标题的新菜单。这些页面将包含互动功能。这些页面将允许用户注册,允许用户查看已注册人员的表格,并允许更改密码。

注册页面

警告

当您最终将数据库迁移到远程主机时,请确保遵守您所在地区或国家的数据保护法,并在注册页面上明确声明用户的个人详细信息不会被共享或出售给其他组织。保护数据的规则因国家而异,您必须阅读并遵守这些规则。通常,组织内任何可以访问用户详细信息的人都必须签署一份文件,同意永远不共享个人信息。管理数据保护法的政府机构可能需要缴纳年度注册费。这些预防措施不适用于使用本书中描述的虚构数据的实验数据库。

注册页面允许用户将他们的个人信息直接输入到数据库的表格中。图 2-8 显示了该界面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-8

注册页面

当用户点击新标题上的注册菜单按钮时,显示如图 2-8 所示的页面。如果用户正确地填写了表单,然后单击 Register 按钮(在输入字段下面),用户的输入将被输入到数据库的 users 表中。然后显示“谢谢”页面。

请注意,注册页面上的注册和新密码按钮现在是多余的,因为用户已经访问了注册页面。显然,用户还没有要更改的密码。多余的按钮将留在本章所有示例的位置,以避免使说明复杂化。多余的按钮将在接下来的两章中删除或更改。

现在我们将检查整个注册页面的代码。

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Template for an interactive web page</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS File -->
  <link rel="stylesheet"
  href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
  integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4"
  crossorigin="anonymous">
<script src="verify.js"></script>
</head>
<body>
    <div class="container" style="margin-top:30px">
    <!-- Header Section -->
    <header class="jumbotron text-center row"
         style="margin-bottom:2px; background:linear-gradient(white, #0073e6); padding:20px;">
         <?php include('header.php'); ?>
    </header>
    <!-- Body Section -->
        <div class="row" style="padding-left: 0px;">
    <!-- Left-side Column Menu Section -->
     <nav class="col-sm-2">
      <ul class="nav nav-pills flex-column">
          <?php include('nav.php'); ?>
      </ul>
    </nav>

    <!-- Validate Input
   <?php
    if ($_SERVER['REQUEST_METHOD'] == 'POST') {                                       //#1
     require('process-register-page.php');
    } // End of the main Submit conditional.
   ?>
   <div class="col-sm-8">
      <h2 class="h2 text-center">Register</h2>

  <form action="register-page.php" method="post" onsubmit="return checked(); >     <!-- #2 -->
     <div class="form-group row">

<label for="first_name" class="col-sm-4 col-form-label">First Name:</label>
    <div class="col-sm-8">
      <input type="text" class="form-control" id="first_name" name="first_name"
          placeholder="First Name" maxlength="30" required
          value="<?php if (isset($_POST['first_name'])) echo $_POST['first_name']; ?>" >
    </div>
  </div>

  <div class="form-group row">
    <label for="last_name" class="col-sm-4 col-form-label">Last Name:</label>
    <div class="col-sm-8">
      <input type="text" class="form-control" id="last_name" name="last_name"
          placeholder="Last Name" maxlength="40" required
          value="<?php if (isset($_POST['last_name'])) echo $_POST['last_name']; ?>">
    </div>
  </div>

<div class="form-group row">
    <label for="email" class="col-sm-4 col-form-label">E-mail:</label>
    <div class="col-sm-8">
      <input type="email" class="form-control" id="email" name="email"
          placeholder="E-mail" maxlength="60" required
          value="<?php if (isset($_POST['email'])) echo $_POST['email']; ?>">
    </div>
  </div>

<div class="form-group row">
    <label for="password1" class="col-sm-4 col-form-label">Password:</label>
    <div class="col-sm-8">
      <input type="password" class="form-control" id="password1" name="password1"
          placeholder="Password" minlength="8" maxlength="12"
          required value="<?php if (isset($_POST['password1'])) echo $_POST['password1']; ?>">
          <span id="message">Between 8 and 12 characters.</span>
  </div>

<div class="form-group row">
    <label for="password2" class="col-sm-4 col-form-label">Confirm Password:</label>
    <div class="col-sm-8">
      <input type="password" class="form-control" id="password2" name="password2"
          placeholder="Confirm Password" minlength="8" maxlength="12" required
          value="<?php if (isset($_POST['password2'])) echo $_POST['password2']; ?>">
    </div>
  </div>

<div class="form-group row">
    <div class="col-sm-12">
       <input id="submit" class="btn btn-primary" type="submit" name="submit" value="Register">
    </div>
 </div>
</form>
</div>

!-- Right-side Column Content Section                                            #3 -->
<?php
if(!isset($errorstring)) {
        echo '<aside class="col-sm-2">';
        include('info-col.php');
        echo '</aside>';
        echo '</div>';
        echo '<footer class="jumbotron text-center row col-sm-14"
                style="padding-bottom:1px; padding-top:8px;">';
 }
 else
 {
        echo '<footer class="jumbotron text-center col-sm-12"
        style="padding-bottom:1px; padding-top:8px;">';
 }
  include('footer.php');
 ?>
</div>
</body>
</html>

Listing 2-8aCreating

the Registration Page (register-page.php)

代码的解释

代码的基本结构与索引文件相同。差异出现在主体中,特别是现在包含供用户注册的表单的中间列。

<?php
 if ($_SERVER['REQUEST_METHOD'] == 'POST') {                                     //#1
  require('process-register-page.php');
 } // End of the main Submit conditional.
?>

在 PHP 中,可以通过表单提交属性将信息传递给一个单独的 PHP 程序来处理表单,也可以通过在表单所在的文件中包含 PHP 代码来处理表单。在这个例子中,从技术上讲,我们将 PHP 代码包含在同一个文件中,因为我们从 process-register-page 文件中提取代码。我们也知道这一点,因为表单的 action 属性调用的是文件本身(form action="register-page.php "),而不是另一个文件(参见下面的部分清单)。如果点击了 submit 按钮,就会执行进程寄存器文件中的 PHP 代码。如果用户没有单击 submit 按钮(可能是第一次显示页面),就会显示表单。让我们来看看表单本身。

<form action="register-page.php" method="post" onsubmit="return checked();>     <!-- #2 -->
     <div class="form-group row">

<label for="first_name" class="col-sm-4 col-form-label">First Name:</label>
    <div class="col-sm-8">
      <input type="text" class="form-control" id="first_name" name="first_name"
          placeholder="First Name" maxlength="30" required
          value="<?php if (isset($_POST['first_name'])) echo $_POST['first_name']; ?>" >
    </div>
  </div>

当用户单击提交按钮时,注册页面会提交给自己。提交时,表单调用 JavaScript 函数 checked()来验证密码。此函数的代码如下所示:

function checked() {
  if (document.getElementById('password1').value ==
    document.getElementById('password2').value) {
    document.getElementById('message').style.color = 'green';
    document.getElementById('message').innerHTML = 'Passwords match';
        return true;
  } else {
    document.getElementById('message').style.color = 'red';
    document.getElementById('message').innerHTML = 'Passwords do not match';
        return false;
  }
}

这段代码是从 verify.js 程序和引导 CSS 代码一起导入的。如果密码匹配,则提交代码。如果密码不匹配,将显示一条错误消息,允许用户更正问题。

我们接下来将看到的 PHP 代码将验证用户提供的信息,并将其插入数据库。表单的每个元素(比如 first_name textbox)中都会发生很多事情。first_name 文本框附有一个标签,该标签使用了四个小的 Boostrap 网格单元。文本框使用八个小网格单元。Boostrap 使用 form-control 类格式化表单和文本框。用户可以输入的名字的最大长度是 30 个字符。required 属性告诉 HTML,在提交信息之前,用户必须在 textbox 中输入信息。

标记为#2 的一行 PHP 代码创建了一个 sticky 元素,保存用户输入的任何内容。这允许页面在重新加载时保留任何条目。每次用户单击提交按钮,页面都会被重新加载。通常这将清除所有条目,因为 HTML 页面不保留信息(断开的数据)。当用户提交表单时,信息通过 POST 方法传递给服务器。我们可以使用 P O S T 方法和元素名称 ( _POST 方法和元素名称( POST方法和元素名称(_POST[‘first_name’])检索表单传递的任何元素。PHP 代码使用 isset 来确定 first_name 文本框中是否有条目。如果有条目,传递的值将被粘回到文本框中。这允许用户看到他们最初输入的内容,并对任何错误消息做出响应和纠正问题,而不必完全重新输入条目。其他文本框的工作方式类似。

注意

将$_POST 中的信息直接显示在网页上可能会带来风险,黑客可以利用它来操纵网页、尝试 CSRF/XSS 攻击以及操纵数据库中的数据。随着章节的进展,数据卫生和安全性将会提高,以减少安全漏洞的机会。本章中的代码不适合在实时网站上使用。

!-- Right-side Column Content Section                                                 #3 -->
<?php
if(!isset($errorstring)) {
        echo '<aside class="col-sm-2">';
        include('info-col.php');
        echo '</aside>';
        echo '</div>';
        echo '<footer class="jumbotron text-center row col-sm-14"
                style="padding-bottom:1px; padding-top:8px;">';
 }
 else
 {
        echo '<footer class="jumbotron text-center col-sm-12"
        style="padding-bottom:1px; padding-top:8px;">';
 }
  include('footer.php');

如果输入的信息未通过验证,将会产生错误消息。正如您将在即将解释的 PHP 代码中看到的,大多数错误消息都放在变量$errorstring 中。如果此变量存在,则存在一个或多个错误。当这种情况发生时,右列(参见前面代码中的#3)不会显示,以便为错误消息提供空间。页脚的列大小也略有调整。

现在让我们看看验证用户提供的信息的 PHP 代码。

<?php
// This script is a query that INSERTs a record in the users table.
// Check that form has been submitted:
        $errors = array(); // Initialize an error array.                                #1
       // Check for a first name:                                                       #2
       $first_name = trim($_POST['first_name']);
       if (empty($first_name)) {
              $errors[] = 'You forgot to enter your first name.';
       }
                // Check for a last name:
       $last_name = trim($_POST['last_name']);
       if (empty($last_name)) {
              $errors[] = 'You forgot to enter your last name.';
       }
       // Check for an email address:
       $email = trim($_POST['email']);
       if (empty($email)) {
              $errors[] = 'You forgot to enter your email address.';
       }
       // Check for a password and match against the confirmed password:               #3
       $password1 = trim($_POST['password1']);
       $password2 = trim($_POST['password2']);
       if (!empty($password1)) {
              if ($password1 !== $password2) {
                       $errors[] = 'Your two passwords did not match.';
               }
        } else {
               $errors[] = 'You forgot to enter your password.';
        }       if (empty($errors)) { // If everything's OK.                            #4
try {
        // Register the user in the database...
        // Hash password current 60 characters but can increase
            $hashed_passcode = password_hash($password1, PASSWORD_DEFAULT);           //#5
             require ('mysqli_connect.php'); // Connect to the db.                    //#6
        // Make the query:                                                              #7
        $query = "INSERT INTO users (userid, first_name, last_name, email, password, registration_date) ";
                $query .="VALUES(' ', ?, ?, ?, ?, NOW() )";                           //#8
        $q = mysqli_stmt_init($dbcon);                                                //#9
        mysqli_stmt_prepare($q, $query);
        // use prepared statement to ensure that only text is inserted
        // bind fields to SQL Statement
        mysqli_stmt_bind_param($q, 'ssss', $first_name, $last_name, $email, $hashed_passcode);
     // execute query
        mysqli_stmt_execute($q);
        if (mysqli_stmt_affected_rows($q) == 1) { // One record inserted               #10
                header ("location: register-thanks.php");
                exit();
                } else { // If it did not run OK.
                // Public message:
                                $errorstring = "<p class='text-center col-sm-8' style='color:red'>";
                        $errorstring .= "System Error<br />You could not be registered due ";
                        $errorstring .= "to a system error. We apologize for any inconvenience.</p>";
                        echo "<p class=' text-center col-sm-2' style='color:red'>$errorstring</p>";
                // Debugging message below do not use in production
                //echo '<p>' . mysqli_error($dbcon) . '<br><br>Query: ' . $query . '</p>';
                               mysqli_close($dbcon); // Close the database connection.
                // include footer then close program to stop execution
                        echo '<footer class="jumbotron text-center col-sm-12"
                                       style="padding-bottom:1px; padding-top:8px;">
                                               include("footer.php");
                                              </footer>';
                             exit();
       }
   }
   catch(Exception $e) // We finally handle any problems here                          #11
   {
     // print "An Exception occurred. Message: " . $e->getMessage();
     print "The system is busy please try later";
   }
   catch(Error $e)
   {
      //print "An Error occurred. Message: " . $e->getMessage();
      print "The system is busy please try again later.";
   }
        } else { // Report the errors.                                                 #12
               $errorstring = "Error! The following error(s) occurred:<br>";
               foreach ($errors as $msg) { // Print each error.
                        $errorstring .= " - $msg<br>\n";
               }
               $errorstring .= "Please try again.<br>";
               echo "<p class=' text-center col-sm-2' style='color:red'>$errorstring</p>";
               }// End of if (empty($errors)) IF.
?>

Listing 2-8bValidating

the Registration Page (process-register-page.php)

注意

在这一点上,初学者可能会感到困惑,因为他们更熟悉从上到下遍历页面的代码。在前面的交互式代码示例中,初学者希望表单字段位于代码页的顶部。事实上,它们在清单中排在最后(在通过 require 方法导入的 PHP 代码之后)。有一个主 if 语句(if($ _ SERVER[’ REQUEST _ METHOD ']= ’ POST '){)控制着程序的流程。如果用户已经访问了页面并点击了提交按钮,那么 If 语句为真,PHP 代码被执行(从包含的文件;上例中的 process_register_page.php)。如果这是用户第一次访问该页面,则 If 语句为 false。因此,执行语句 else 部分中的 HTML 代码。在提交表单和执行 PHP 代码之前,HTML5 代码还会对用户输入的信息进行一些有限的验证。此外,PHP 代码将验证从 web 页面收到的是“好的”、经过验证的和经过净化的信息。

看起来我们重复核实了两次信息。然而,使用 HTML5(可能还有一些 JavaScript)来首次尝试验证信息可以减少服务器调用并加快验证过程。我们仍然需要在服务器上再次验证信息,因为在信息到达之前,它可能被有意或无意地操纵。

代码的解释

清单中的注释解释了许多 PHP 语句,但是有些项目需要进一步注释。PHP 代码可能看起来非常复杂,但是我们将把它分成更小的部分来帮助你理解它。让我们现在开始吧。

$errors = array(); // Initialize an error array.                                       #1

数组是一种将多项相似信息存储到一个存储位置的方法。在我们的例子中,我们使用数组来存储任何错误消息。前一行初始化数组,并将其命名为 e r r o r s 。从技术上讲, errors。从技术上讲, errors。从技术上讲,errors 现在指向内存中为数组保留的区域。本章末尾和附录中提供了一些有关数组及其用法的附加信息。

// Check for a first name:                                                             #2
$first_name = trim($first_name);
if (empty($first_name)) {
       $errors[] = 'You forgot to enter your first name.';
}

像$_POST[‘first_name’]这样的全局变量中的元素总是用方括号括起来。这是超级全局变量的一个特性。从技术上讲,从我们的表单传递到 PHP 代码的所有内容都被传递到一个 post 数组中(这就是我们使用方括号而不是圆括号的原因)。然后,我们使用 POST 超级全局语句从数组中检索我们想要的内容。

f i r s t n a m e = t r i m 开头的代码是一条指令,用于从用户的 f i r s t n a m e 条目的开头和结尾修剪 ( 删除 ) 任何空格或其他空白。输入数据开头和结尾的空格是不需要的字符。然后将修剪后的名字赋给变量 first_name = trim 开头的代码是一条指令,用于从用户的 first_name 条目的开头和结尾修剪(删除)任何空格或其他空白。输入数据开头和结尾的空格是不需要的字符。然后将修剪后的名字赋给变量 firstname=trim开头的代码是一条指令,用于从用户的firstname条目的开头和结尾修剪(删除)任何空格或其他空白。输入数据开头和结尾的空格是不需要的字符。然后将修剪后的名字赋给变量first_name。为了增强安全性,我们应该额外删除无效字符。然而,就目前而言,让我们尽量保持简单。前面显示的格式也用于姓氏和电子邮件。

注意

任何采用 P O S T [ ] 格式的变量,如 _POST[ ]格式的变量,如 POST[]格式的变量,如_POST[‘first_name’],都是一个全局变量,可通过网站上的页面访问。普通变量的格式为 f i r s t n a m e ,只能通过加载出现它们的页面来访问它们。 first_name,只能通过加载出现它们的页面来访问它们。 firstname,只能通过加载出现它们的页面来访问它们。_POST 将通过 POST 数组提取从 HTML 表单传递的信息,当用户单击 submit 按钮时,会自动填充该数组。方括号中包含的变量名(first_name)必须与 HTML 输入语句的 name 参数完全匹配。

// Check for a password and match against the confirmed password:                      #3
$password1 = trim($_POST['password1']);
$password2 = trim($_POST['password2']);
if (!empty($password1)) {
         if ($password1 !== $password2) {
                  $errors[] = 'Your two password did not match.';
         }
} else {
         $errors[] = 'You forgot to enter your password.';
}

两个新密码都去掉了不必要的空格。如果密码 1 不为空。空),则执行两个新密码的比较。那个!==(一!和两个=)符号确定表单中输入的密码是否相同。这种不等于的格式确保了大写和小写是相同的。它还确保数据类型(字符串与整数相比)是相同的。如果密码不同,将在错误数组中显示一条错误消息。

if (empty($errors)) { // If everything's OK.                                           #4

这段代码位于成功部分。当所有字段都已正确填写时,该部分开始注册过程——在这种情况下,$errors 数组为空。这一行是说“因为没有错误,我们可以连接到数据库并插入用户的数据。”

$hashed_passcode = password_hash($password, PASSWORD_DEFAULT);                       //#5

我们必须确保所有密码尽可能安全。前面的代码使用 PHP 密码哈希方法将用户输入的密码转换为安全密码。PASSWORD_DEFAULT 将总是包含最新的 PHP 散列码。在数据库表中,我们将密码字段设置为包含 60 个字符。将来,新的散列技术可能会要求您增加该字段的大小。

require ('mysqli_connect.php'); // Connect to the db.                                //#6

在这一行中,数据库连接是用 require()而不是 include()建立的,这样,如果建立了连接,脚本就会运行,但是如果没有连接,脚本就不会运行。这是因为如果数据库不可访问(因为无法将数据插入到表中),那么继续操作就没有意义。当需要包含一些重要的东西时,我们使用 require()。我们本可以在这里使用 require_once,因为我们只想将这段代码引入一次,以建立到数据库的连接。

// Make the query:                                                                    #7
$query = "INSERT INTO users (userid, first_name, last_name, email, password, registration_date) ";

如果成功连接到数据库,这一行就准备将数据输入到名为 users 的表中。单词 query 有时表示“查询”,但更多时候表示“做点什么”。在这种情况下,它意味着“使用以下数据插入新记录…”这一行是“插入到名为 users 的表中,在标有useridfirst_name等的列标题下……”该查询被分配给一个变量$query。第 7 行和第 8 行实际上是 SQL 准备的查询。这展示了 HTML、PHP 和 SQL 是如何很好地协同工作的。

$query .="VALUES(' ', ?, ?, ?, ?, NOW() )";                                          //#8

这段 SQL 代码为要输入的数据提供了一个存放位置。我们使用预准备语句,因为它永远不会执行已经绑定到位置的数据。标记存在)。这使得黑客无法在我们的 SQL 字符串中插入额外的 SQL 语句来破坏我们的数据。请注意,第一个值故意为空(用引号括起来,中间有一个空格),因为它是由 MySQL 或 MariaDB 数据库管理系统自动递增并输入的字段,而不是由用户输入的。NOW 函数自动将数据和时间放入数据库表的 registration_date 列中。

$q = mysqli_stmt_init($dbcon);                                                       //#9
mysqli_stmt_prepare($q, $query);
// use prepared statement to ensure that only text is inserted
// bind fields to SQL Statement
mysqli_stmt_bind_param($q, 'ssss', $first_name, $last_name, $email, $hashed_passcode);
// param must include four variables to match the four s parameters
// execute query
mysqli_stmt_execute($q);

来自之前包含的文件( mysqli_connect.php )的连接字符串被附加到 mysqli,并且可以用 q 变量引用。然后 q 变量引用。然后 q变量引用。然后query 字符串被声明为一个准备好的语句(表示变量将被插入到代码中),并与数据库连接字符串相关联。变量( f i r s t n a m e , first_name, firstnamelast_name, e m a i l , email, emailhashed_passcode)附加到准备好的 SQL 语句中。注意,s 符号表示每个变量都包含一个字符串。要表示数字信息,可以使用 I(整数)或 d(双精度)。b 也可用于 BLOB。然后对数据库表执行该语句。

if (mysqli_stmt_affected_rows($q) == 1) { // One record inserted                      #10
       header ("location: register-thanks.php");
       exit();

如果操作成功,数据库将被关闭。那么 header 方法会用一个“谢谢”页面替换当前的注册页面。header 方法产生一个请求页面的 HTML Get 消息。exit 命令将关闭当前页面,因为它不再被使用。

catch(Exception $e) // We finally handle any problems here                            #11
   {
     // print "An Exception occurred. Message: " . $e->getMessage();
     print "The system is busy please try later";
   }
   catch(Error $e)
   {
      //print "An Error occurred. Message: " . $e->getMessage();
      print "The system is busy please try again later.";
   }

如果在执行 SQL 命令时出现错误或其他错误或异常,代码流将跳转到 catch 块来处理问题。如上所述,对于测试,我们可以显示我们的错误,但是当我们切换到生产时,我们将显示一个通用的“系统当前不可用”消息,并将问题记录在错误日志中。

} else { // Report the errors.                                                        #12

               $errorstring = "Error! The following error(s) occurred:<br>";
               foreach ($errors as $msg) { // Print each error.
                       $errorstring .= " - $msg<br>\n";
               }
               $errorstring .= "Please try again.<br>";
               echo "<p class='text-center col-sm-2' style='color:red'>$errorstring;</p>";
               }// End of if (empty($errors)) IF.

如果有用户错误(无效条目),消息将存在于 e r r o r 数组中。当数组中有条目时,将实现 e l s e 结构。 f o r e a c h 循环遍历 error 数组中。当数组中有条目时,将实现 else 结构。foreach 循环遍历 error数组中。当数组中有条目时,将实现else结构。foreach循环遍历errors 数组,如果发现任何错误消息,就会显示(回显)在屏幕上(foreach 是一个不带空格的单词)。

在 HTML 代码中检查$errorstring 变量,以确定是否显示右列。如果有错误,右列不会显示,以便为错误消息留出空间。

您可能想知道为什么我们要创建 PHP 代码来进行与所示 HTML5 代码相似的验证。PHP 代码在服务器上被解释和执行。HTML 代码在浏览器中执行。即使代码在浏览器中得到验证,它也可能在到服务器 PHP 程序的路径上被破坏。您应该使用 HTML(和 JavaScript)进行验证,以便更快地验证和响应用户。它还减少了服务器调用。

PHP 关键字 echo

在前面的代码中,关键字 echo 出现在许多地方。这是 PHP 告诉浏览器“在屏幕上显示一些东西”的方式。一些设计师使用可选的关键字 print,但本书通常会使用 echo,因为我们发现初学者会将它与将东西发送到喷墨或激光打印机的命令混淆;硬拷贝打印是 PHP 做不到的。

注意

您想要的任何 HTML 代码都可以放在 echo 语句中。echo 将内容写入 HTML 文档(虚拟地,而不是字面地),以便可以在浏览器中显示。

当需要换行时,初学者可能会对 echo 的行为感到困惑。例如,以下代码不显示任何行间距:

echo "I found that PHP ";
echo "was much easier to learn than Perl";

浏览器显示如下:

我发现 PHP 比 Perl 容易学得多

要向下推第二行,必须插入换行符标记
,如下所示:

echo "I found that PHP<br>";
echo "was much easier to learn than Perl";

浏览器显示如下:

我发现 PHP 比 Perl 容易学得多

“谢谢”页面

图 2-9 显示了“感谢”页面。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-9

“谢谢”页面

注册页面使用 PHP header 语句调用“谢谢”页面。您可以在注册页面代码的成功部分找到这一点。为方便起见,我们在此重复一遍:

$dbcon->close();
header ("location: register-thanks.php");
exit();

清单 2-9 显示了完整的“谢谢”页面代码。

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Template for an interactive web page</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS File -->
  <link rel="stylesheet"
  href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
  integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4"
  crossorigin="anonymous">
</head>
<body>
<div class="container" style="margin-top:30px">
<!-- Header Section -->
<header class="jumbotron text-center row"
style="margin-bottom:2px; background:linear-gradient(white, #0073e6); padding:20px;">
  <?php include('header.php'); ?>
</header>
<!-- Body Section -->
  <div class="row" style="padding-left: 0px;">
<!-- Left-side Column Menu Section -->
  <nav class="col-sm-2">
      <ul class="nav nav-pills flex-column">
         <?php include('nav.php'); ?>
      </ul>
  </nav>
<!-- Center Column Content Section -->
  <div class="col-sm-8 text-center">
      <h2>Thank you for registering</h2>
      On the Home Page, you will now be able to login and add new quotes to the message board.
<!-- login does not yet work, nut will in the next chapter -->
  </div>
<!-- Right-side Column Content Section -->
      <aside class="col-sm-2">
           <?php include('info-col.php'); ?>
      </aside>
  </div>
<!-- Footer Content Section -->
     <footer class="jumbotron text-center row"
          style="padding-bottom:1px; padding-top:8px;">
          <?php include('footer.php'); ?>
     </footer>
</div>
</body>
</html>

Listing 2-9Creating the “Thank You” Page (register-thanks.php)

如您所见,该页面是根据模板创建的,只做了一些更改。这保持了网站应有的一致性。我们现在来看看当服务器收到无效信息时会发生什么。我们将使用一个数组来显示错误消息。

显示数组中收集的错误信息

如果服务器上的所有字段都收到无效信息(空值),将会产生多个错误。相应的错误消息被插入到错误数组中。然后显示这些信息,以便用户了解情况。如果只显示第一个错误,然后在第二次单击 Register 按钮后,又显示第二个错误,以此类推,那就太烦人了。

图 2-10 显示了出现这种情况时显示的错误示例。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-10

用户未能输入任何数据时显示的错误

错误显示在注册页面上。如前所述,当出现用户错误时,右栏被删除,以提供一个显示消息的区域。错误可能表明浏览器和服务器之间的数据以某种方式被破坏。如果用户输入不正确的信息并点击提交按钮,HTML5 和 JavaScript 代码将处理大多数验证情况并显示错误消息。如果您想测试更多的服务器错误消息,请从每个 HTML 文本框中删除必需的属性。

散列密码

所有密码都必须经过哈希处理,以防黑客发现。从 PHP 5.5 开始,开发人员创建了一种新的密码散列(编码)方法。这种方法被认为是安全的,是散列密码的最佳方法之一。格式如下:

$hashedPassword = password_hash($password1, PASSWORD_DEFAULT);

在本例中,$password1 是密码的未哈希版本。PASSWORD_DEFAULT 是一个 PHP 常量。随着安全性的变化,PHP 开发人员计划保持编码格式不变,并改变 PHP 常量来表示任何新的散列方案。password_hash 方法将用户输入的密码转换为安全的散列密码,然后可以保存在数据库表中。我们将在后面发现,我们必须使用 password_verify 方法来确定用户输入的密码是否与存储在数据库中的散列密码相匹配。

查看成员记录

当用户注册后,网站管理员可以使用 phpMyAdmin 来查看该表及其条目。我们假设一个用户在 2018 年 4 月 26 日注册,他们输入了以下信息:

名字:史蒂夫姓氏:约翰逊,电子邮件:sjohnson@sjohnson.com,密码:aaaaaaaa

phpMyAdmin 将允许您查看用户表中的每条记录。访问 phpMyAdmin,选择 simpledb 数据库,单击 users 表,然后单击 Browse 选项卡。图 2-11 显示了斯蒂夫·约翰森的参赛作品。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-11

在 phpMyAdmin 中查看记录

请注意,密码 aaaaaaaa 被散列为$ 2y $ 10 $ lEmRKPYfu/nb 6 ect BMP 7 youizezdyucnzkrmebnq 6 nrhdkjhdegmk。

通常,无法访问 phpMyAdmin 的网站设计人员和数据库管理员可能还需要查看由所有注册用户创建的数据库条目表。下一个示例提供了这样做的信息。

“查看用户”页面

点击查看用户按钮,显示注册用户列表,如图 2-12 所示。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-12

将显示一个用户表

当用户单击右上角菜单上的“查看用户”按钮时,将显示该表。清单 2-10 显示了显示用户表的代码。

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Template for an interactive web page</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <!-- Bootstrap CSS File -->
  <link rel="stylesheet"
  href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
  integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4"
  crossorigin="anonymous">
</head>
<body>
<div class="container" style="margin-top:30px">
<!-- Header Section -->
<header class="jumbotron text-center row"
style="margin-bottom:2px; background:linear-gradient(white, #0073e6); padding:20px;">
  <?php include('header.php'); ?>
</header>
<!-- Body Section -->
  <div class="row" style="padding-left: 0px;">
<!-- Left-side Column Menu Section -->
  <nav class="col-sm-2">
      <ul class="nav nav-pills flex-column">
                <?php include('nav.php'); ?>
      </ul>
  </nav>
<!-- Center Column Content Section -->
  <div class="col-sm-8">
  <h2 class="text-center">These are the registered users</h2>
<p>
<?php
try {
// This script retrieves all the records from the users table.
require('mysqli_connect.php'); // Connect to the database.
// Make the query:
// Nothing passed from user safe query                                                 #1
$query = "SELECT CONCAT(last_name, ', ', first_name) AS name, ";
$query .= "DATE_FORMAT(registration_date, '%M %d, %Y') AS ";
$query .= "regdat FROM users ORDER BY registration_date ASC";
$result = mysqli_query ($dbcon, $query); // Run the query.
if ($result) { // If it ran OK, display the records.
// Table header.                                                                       #2
echo '<table class="table table-striped">
<tr><th scope="col">Name</th><th scope="col">Date Registered</th></tr>';
// Fetch and print all the records:                                                    #3
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
echo '<tr><td>' . $row['name'] . '</td><td>' . $row['regdat'] . '</td></tr>'; }
    echo '</table>'; // Close the table so that it is ready for displaying.
    mysqli_free_result ($result); // Free up the resources.
} else { // If it did not run OK.
// Error message:
echo '<p class="error">The current users could not be retrieved. We apologize';
echo ' for any inconvenience.</p>';
// Debug message:
// echo '<p>' . mysqli_error($dbcon) . '<br><br>Query: ' . $q . '</p>';
exit;
} // End of if ($result)
mysqli_close($dbcon); // Close the database connection.
}
catch(Exception $e) // We finally handle any problems here
   {
     // print "An Exception occurred. Message: " . $e->getMessage();
     print "The system is busy please try later";
   }
   catch(Error $e)
   {
      //print "An Error occurred. Message: " . $e->getMessage();
      print "The system is busy please try again later.";
   }
?>

  </div>
<!-- Right-side Column Content Section -->
        <aside class="col-sm-2">
      <?php include('info-col.php'); ?>
        </aside>
  </div>
<!-- Footer Content Section -->
<footer class="jumbotron text-center row"
style="padding-bottom:1px; padding-top:8px;">
  <?php include('footer.php'); ?>
</footer>
</div>
</body>
</html>

Listing 2-12Displaying a Table of Registered Members on the Screen (register-view-users.php)

代码的解释

您之前已经看到了大部分代码,但下面是对新项目的解释:

// Make the query:
// Nothing passed from user safe query                                                 #1
$query = "SELECT CONCAT(last_name, ', ', first_name) AS name, ";
$query .= "DATE_FORMAT(registration_date, '%M %d, %Y') AS ";
$query .= "regdat FROM users ORDER BY registration_date ASC";
$result = mysqli_query ($dbcon, $query); // Run the query.
if ($result) { // If it ran OK, display the records.

SQL 查询选择姓,然后是逗号,然后是名,并将其串在一起(串联),同时选择注册数据。它将这些文件的临时标题设置为 name 和 regdat。它以月-日-年的格式重新排列注册日期。它使用 select 查询从 users 表中提取信息。最后,它要求每一行信息都按照升序(ASC)日期顺序显示(最早的排在最前面)。要首先显示最新登记的记录(降序),可以使用 DESC 而不是 ASC。由于查询没有从用户那里收集任何信息,我们不需要创建一个准备好的语句,传递注册页面中显示的变量。这个查询字符串不能被破解。

// Table header.                                                                       #2
echo '<table class="table table-striped">
<tr><th scope="col">Name</th><th scope="col">Date Registered</th></tr>';

此代码块使用引导类表显示(回显)该表,并剥离表以格式化显示。列标题 Name 和 Date Registered 也被声明为显示的表的第一行。

// Fetch and print all the records:                                                    #3
while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
echo '<tr><td>' . $row['name'] . '</td><td>' . $row['regdat'] . '</td></tr>'; }

这段代码循环遍历这些行,并在数据库表中的行数据可用时显示它们。MYSQLI_ASSOC 创建一个关联数组。PHP 关联数组使用关键字(单词)作为索引。例如,$row[‘name’]包含从 users 表中提取的串联的名字和姓氏(参见#1)。

用户有时想要更改他们的密码。下一节将演示如何做到这一点。

“更改密码”页面

当用户点击标题右上角菜单上的新密码按钮时,出现如图 2-13 所示的表单。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-13

更改密码表单

只有当用户知道他们当前的密码和电子邮件地址时,该表单才适用。如果他们忘记了密码,就需要一种不同的方法,这将在后面的章节中讨论。然而,如果用户不喜欢他们最初选择的密码,这个“新密码”页面是有用的。清单 2-13a 给出了修改密码表单的 HTML 代码。

<!DOCTYPE html>
<html lang="en">
<head>
 <title>Template for an interactive web page</title>
 <meta charset="utf-8">
 <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 <!-- Bootstrap CSS File -->
 <link rel="stylesheet"
 href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
 integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4"
 crossorigin="anonymous">
<script src="verify.js"></script>
</head>
<body>
<div class="container" style="margin-top:30px">
<!-- Header Section -->
<header class="jumbotron text-center row col-sm-14"
style="margin-bottom:2px; background:linear-gradient(white, #0073e6); padding:20px;">
 <?php include('header.php'); ?>
</header>
<!-- Body Section -->
 <div class="row" style="padding-left: 0px;">
<!-- Left-side Column Menu Section -->
  <nav class="col-sm-2">
     <ul class="nav nav-pills flex-column">
        <?php include('nav.php'); ?>
      </ul>
  </nav>
 <!-- Validate Input -->
   <?php
  if ($_SERVER['REQUEST_METHOD'] == 'POST') {
   require('process-change-password.php');                                           //#1
  } // End of the main Submit conditional.
  ?>
<div class="col-sm-8">
<h2 class="h2 text-center">Change Password</h2>
<form action="change-password.php" method="post" name="regform"
 id="regform" onsubmit="return checked();">
<div class="form-group row">
    <label for="email" class="col-sm-4 col-form-label">E-mail:</label>
        <div class="col-sm-8">
        <input type="email" class="form-control" id="email" name="email"
        placeholder="E-mail" maxlength="60" required
        value="<?php if (isset($_POST['email'])) echo $_POST['email']; ?>">
        </div>
 </div>
 <div class="form-group row">
        <label for="password" class="col-sm-4 col-form-label">Current Password:</label>
        <div class="col-sm-8">
        <input type="password" class="form-control" id="password" name="password"
        placeholder="Password" minlength="8" maxlength="12"
        required value="<?php if (isset($_POST['password'])) echo $_POST['password']; ?>">
        </div>
 </div>
<div class="form-group row">
        <label for="password1" class="col-sm-4 col-form-label">New Password:</label>
        <div class="col-sm-8">
         <input type="password" class="form-control" id="password1" name="password1"
          placeholder="Password" minlength="8" maxlength="12"
          required value="<?php if (isset($_POST['password1'])) echo $_POST['password1']; ?>">
          <span id="message">Between 8 and 12 characters.</span>
</div>
<div class="form-group row">
         <label for="password2" class="col-sm-4 col-form-label">Confirm Password:</label>
         <div class="col-sm-8">
                 <input type="password" class="form-control" id="password2" name="password2"
          placeholder="Confirm Password" minlength="8" maxlength="12" required
          value="<?php if (isset($_POST['password2'])) echo $_POST['password2']; ?>">
        </div>
 </div>
<div class="form-group row">
        <div class="col-sm-12">
        <input id="submit" class="btn btn-primary" type="submit" name="submit"
        value="Change Password">
        </div>
</div>
</form>
</div>
<!-- Right-side Column Content Section -->
<?php
 if(isset($errorstring)) {
         echo '<footer class="jumbotron text-center col-sm-12"
        style="padding-bottom:1px; padding-top:8px;">';
 }
 else
 {
        echo '<aside class="col-sm-2">';
        include('info-col.php');
        echo '</aside>';
        echo '</div>';
        echo '<footer class="jumbotron text-center row col-sm-14"
        style="padding-bottom:1px; padding-top:8px;">';
 }
  include('footer.php');
 ?>
</footer>
</div>
</body>
</html>

Listing 2-13aCreating a Page to Allow Users to Change a Password (change-password.php)

代码的解释

本节给出了代码的解释。

  if ($_SERVER['REQUEST_METHOD'] == 'POST') {
   require('process-change-password.php');                                           //#1
  } // End of the main Submit conditional.

更改密码页面的 HTML 类似于注册页面的 HTML。主要区别是 PHP 代码现在是从 process-change-password 文件导入的。让我们看看清单 2-13b 中这个文件的内容。

<?php
// This script is a query that UPDATES the password in the users table.
// Check that form has been submitted:
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
require ('mysqli_connect.php'); // Connect to the db.
$errors = array(); // Initialize an error array.
// Check for an email address:
$email = trim($_POST['email']);
if (empty($email)) {
        $errors[] = 'You forgot to enter your email address.';
}
// Check for a password and match against the confirmed password:
$password = trim($_POST['password']);
if (empty($password)) {
        $errors[] = 'You forgot to enter your old password.';
}
// Prepare and check new password                                                      #1
$new_password = trim($_POST['password1']);
$verify_password = trim($_POST['password2']);
if (!empty($new_password)) {
        if (($new_password != $verify_password) ||
        ( $password == $new_password ))
        {
$errors[] = 'Your new password did not match the confirmed password and/or ';
$errors[] = 'Your old password is the same as your new password.';
        }
} else {
        $errors[] = 'You did not enter a new password.';
}if (empty($errors)) { // If everything's OK.
try {
 // Check that the user has entered the right email address/password combination:      #2
 $query = "SELECT userid, password FROM users WHERE ( email=? )";
$q = mysqli_stmt_init($dbcon);
mysqli_stmt_prepare($q, $query);
 // use prepared statement to ensure that only text is inserted
 // bind fields to SQL Statement
mysqli_stmt_bind_param($q, 's', $email);
 // execute query
 mysqli_stmt_execute($q);
$result = mysqli_stmt_get_result($q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
if ((mysqli_num_rows($result) == 1)                                                  //#3
          && (password_verify($password, $row['password'])))
          {    // Found one record
         // Change the password in the database...
         // Hash password current 60 characters but can increase
         $hashed_passcode = password_hash($new_password, PASSWORD_DEFAULT);
         // Make the query:
         $query = "UPDATE users SET password=? WHERE email=?";
         $q = mysqli_stmt_init($dbcon);
         mysqli_stmt_prepare($q, $query);
         // use prepared statement to ensure that only text is inserted
         // bind fields to SQL Statement
         mysqli_stmt_bind_param($q, 'ss', $hashed_passcode, $email);
         // execute query
         mysqli_stmt_execute($q);
         if (mysqli_stmt_affected_rows($q) == 1) {   // one row updated                #4
                       // Thank you
                      header ("location: password-thanks.php");
                                exit();
        } else { // If it did not run OK.                                               #5
               // Public message:
               $errorstring = "System Error! <br /> You could not change password due ";
               $errorstring .= "to a system error. We apologize for any inconvenience.</p>";
               echo "<p class='text-center col-sm-2' style='color:red'>$errorstring</p>";
               // Debugging message below do not use in production
               //echo '<p>' . mysqli_error($dbcon) . '<br><br>Query: ' . $query . '</p>';
               // include footer then close program to stop execution
               echo '<footer class="jumbotron text-center col-sm-12"
                       style="padding-bottom:1px; padding-top:8px;">
                       include("footer.php");
                       </footer>';
               exit();
        }
         } else { // Invalid email address/password combination.
               $errorstring = 'Error! <br /> ';
                       $errorstring .= 'The email address and/or password do not match those on file.';
               $errorstring .= " Please try again.";
               echo "<p class='text-center col-sm-2' style='color:red'>$errorstring</p>";
} }
   catch(Exception $e) // We finally handle any problems here
   {
     // print "An Exception occurred. Message: " . $e->getMessage();
     print "The system is busy please try later";
   }
   catch(Error $e)
   {
      //print "An Error occurred. Message: " . $e->getMessage();
      print "The system is busy please try again later.";
   }
   } else { // Report the errors.                                                      #6
        //header ("location: register-page.php");
        $errorstring = "Error! The following error(s) occurred:<br>";
        foreach ($errors as $msg) { // Print each error.
                $errorstring .= " - $msg<br>\n";
        }
        $errorstring .= "Please try again.<br>";
        echo "<p class=' text-center col-sm-2' style='color:red'>$errorstring</p>";
        }// End of if (empty($errors)) IF.
} // End of the main Submit conditional.
?>

Listing 2-13bProcessing the Changed Password (process-change-password.php)

代码的解释

本节解释代码。

// Prepare and check new password                                                      #1
$new_password = trim($_POST['password1']);
$verify_password = trim($_POST['password2']);
if (!empty($new_password)) {
        if (($new_password != $verify_password) ||
        ( $password == $new_password ))
        {
$errors[] = 'Your new password did not match the confirmed password and/or ';
$errors[] = 'Your old password is the same as your new password.';
       }
} else {
       $errors[] = 'You did not enter a new password.';

前两行代码删除字符串中的所有空格。在后面的章节中,我们将验证密码的格式是否正确。但是,目前我们使用的是准备好的语句;因此,用户试图插入文本框的任何代码都不会被执行。如果两个新密码匹配,新密码将放在$password 中。如果它们不匹配,一个错误被发送回网页。

(
// Check that the user has entered the right email address/password combination:       #2
 $query = "SELECT userid, password FROM users WHERE ( email=? )";
$q = mysqli_stmt_init($dbcon);
mysqli_stmt_prepare($q, $query);
 // use prepared statement to ensure that only text is inserted
 // bind fields to SQL Statement
mysqli_stmt_bind_param($q, 's', $email);
 // execute query
 mysqli_stmt_execute($q);
$result = mysqli_stmt_get_result($q);
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);

如果所有验证都成功,我们必须确保用户提供了现有的电子邮件和密码。前面代码中的第 2 行创建了一个准备好的 select 语句,该语句使用提供的电子邮件从 users 表中提取用户名和密码。如果 SQL 语句返回一个结果,我们就验证了该电子邮件存在于数据库中。

if ((mysqli_num_rows($result) == 1)                                                  //#3
          && (password_verify($password, $row['password'])))
          {    // Found one record
        // Change the password in the database...
         // Hash password current 60 characters but can increase
         $hashed_passcode = password_hash($new_password, PASSWORD_DEFAULT);
        // Make the query:
        $query = "UPDATE users SET password=? WHERE email=?";
        $q = mysqli_stmt_init($dbcon);
        mysqli_stmt_prepare($q, $query);
        // use prepared statement to ensure that only text is inserted
        // bind fields to SQL Statement
        mysqli_stmt_bind_param($q, 'ss', $hashed_passcode, $email);
        // execute query
        mysqli_stmt_execute($q);

如果我们从数据库表中得到一个结果,我们还需要验证密码是否正确。if 语句使用 password_verify 函数将旧密码与表中的密码进行比较。这是必要的,因为存储在表中的密码是散列的,而用户提供的密码不是散列的。如果匹配,则对新密码进行哈希运算。创建一个准备好的 SQL 更新查询来更改 users 表中的密码。然后执行该查询。

if (mysqli_stmt_affected_rows($q) == 1) {    // one row updated                        #4
                       // Thank you
                       header ("location: password-thanks.php");
                                exit();

如果 SQL 更新查询成功,将显示“谢谢”页面。header 函数使用 HTML Get 命令调用密码感谢页面。使用 exit 方法关闭当前页面。

} else { // If it did not run OK.                                                      #5
               // Public message:
               $errorstring = "System Error! <br /> You could not change password due ";
               $errorstring .= "to a system error. We apologize for any inconvenience.</p>";
               echo "<p class='text-center col-sm-2' style='color:red'>$errorstring</p>";
               // Debugging message below do not use in production
               //echo '<p>' . mysqli_error($dbcon) . '<br><br>Query: ' . $query . '</p>';
               // include footer then close program to stop execution
               echo '<footer class="jumbotron text-center col-sm-12"
                       style="padding-bottom:1px; padding-top:8px;">
                       include("footer.php");
                       </footer>';
               exit();

如果没有行被更改,则查询不会成功执行。else 语句捕获这种情况,并显示一条系统错误语句。因为这是一个严重的错误,所以为页面提供了一个页脚,并关闭了代码(exit())。这将关闭对数据库的任何访问,并且不会让任何其他代码执行。注释中列出了调试代码,这些代码将提供有关该问题的更多信息。此代码不应在实时代码中激活。提供的错误信息可能会显示代码和数据库连接信息,这将是一个严重的安全违规。

} else { // Report the errors.                                                         #6
        //header ("location: register-page.php");
        $errorstring = "Error! The following error(s) occurred:<br>";
        foreach ($errors as $msg) { // Print each error.
                $errorstring .= " - $msg<br>\n";
        }
        $errorstring .= "Please try again.<br>";
        echo "<p class=' text-center col-sm-2' style='color:red'>$errorstring</p>";
        }// End of if (empty($errors)) IF.

代码的最后一部分类似于注册页面。它显示用户输入无效信息时导致的任何错误消息。这些错误与表单显示在同一个页面上,以便用户进行更正并重新提交信息。

确认密码更改成功

如果密码更改成功,显示如图 2-14 所示的页面。

图 2-14 所示页面与注册“感谢”页面相似。您可以从本章的下载文件中查看代码。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

图 2-14

密码已被更改

测试教程的页面

要查看交互式页面的工作情况,请双击桌面上的 XAMPP 或 easyPHP 图标。当控制面板出现时,检查 Apache 和 MySQL/MariaDB 是否正在运行。

在浏览器的地址栏中输入http://localhost/simpledb/index.php,然后点击注册按钮,这样就可以输入一些用户。

为了节省你的时间和虚构人物的努力,使用表 2-1 中提供的建议。

表 2-1

输入成员详细信息的建议

|

名字

|

电子邮件

|

密码

|
| — | — | — |
| 麦克·罗斯 | 米克尔@myisp.com | W1llgat3s |
| 橄榄枝 | obranch@myisp.com.uk | 第 1 年第 0 季第 3 集 |
| 弗兰克·弗雷泽 | finsin @ my ISP . net | P3 PFM 300 型核潜艇 |
| 周年纪念日 | aversary@myisp.com | B1 港币 3yg1rl |
| 特里·菲德 | tfide @ my ISP . com | 刀疤 3dst1ff |
| 玫瑰(灌木)丛 | rbush@myisp.co.uk | R3 db 100 ms |

现在用户表中有了更多的数据,运行 XAMPP 或 EasyPHP,使用浏览器单击菜单项显示注册用户表。

小费

毫无疑问,当您创建代码并测试它时,您会遇到错误消息。如需帮助,请参考第十二章中的故障排除提示。

在本章的前面,我们承诺了更多关于 PHP 数组使用的信息。下一节将帮助您理解这个最有用的特性的其他方面。

关于数组的更多信息

本书通篇都在使用数组,如果你访问论坛寻求 PHP 使用建议,你会遇到许多数组。

数组是内存中存放多个相似项目的地方(比如下面显示的谷物)。这些项目以相同的变量名(谷物)组合在一起。要引用数组中的单个元素,我们必须使用一个键(下标)。在 PHP 中,下标可以是一个数字( 谷类 [ 1 ] ) 或者一个字符串 ( 比如 谷类[1])或者一个字符串(比如 谷类[1])或者一个字符串(比如row[‘name’])。我们将在整本书中使用这两种格式。

数组可以通过几种方式填充元素。我们创建了一个名为$谷类的数组,如下所示:

<?php
$cereals = array();
?>

可以将一些元素插入到数组中,如下所示(注意,第一个数组元素通常为零):

<?php
$cereals = array[];
$cereals[0] = "oats";
$cereals[1] = "barley";
$cereals[2] = "corn";
$cereals[3] = "wheat";
?>

若要显示该数组的元素,请在结束标记前插入以下代码。>:

echo "$cereals[0] " . "$cereals[1]  " . "$cereals[2]  " .  "$cereals[3]";?>

显示屏将显示以下内容:

燕麦大麦玉米小麦

echo 语句使用连接符号将字符串连接在一起。).但是,在 PHP 中,我们也可以将程序变量(如$name)放在引号内,如下所示:

$first_name = "Fred";
$middle_name = "Adam";
$last_name = "Smith";

echo "$first_name $middle_name $last_name";

PHP 还允许我们在不提供索引号的情况下将项目插入到一个数字数组中。

$error[] = "One error";
$error[] = "Another error";

PHP 将自动用 0 索引第一项,用 1 索引第二项。因此,我们可以显示如下值:

echo "$error[0] $error[1]";

关于数组还有很多需要学习的地方。然而,这些少量的知识应该可以帮助您理解我们在从数据库表中提取信息以及加载和显示错误消息时是如何使用数组的。

警告

本章中输入数据库表格的数据没有被完全过滤。它们被有意地去掉了大部分过滤器和清洁剂,因此基本的过程是整洁的,清晰可见的。越来越多的安全和过滤功能将在后面的章节中添加。

摘要

在这一章中,你创建了你的第一个交互式页面并测试了它们。您学到了更多关于 PHP 的知识——特别是,您应该已经掌握了在代码中寻找和识别逻辑模式的思想。您学习了如何使用几个 PHP 函数:include()、required()和 echo()。你也学到了很多 mysqli 函数。您发现了 PHP 条件对于创建交互式数据库页面的重要性。您还了解了如何在将数据发送到数据库表之前检查用户是否输入了信息。在下一章,我们将增加一些额外的安全性,并演示用户和管理员如何登录和注销网站上的个人页面。您还将学习如何删除或替换标题菜单中的冗余链接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值