PHP 零基础初学者手册(一)
原文:PHP for Absolute Beginners
协议:CC BY-NC-SA 4.0
一、设置 PHP 开发环境
构建一个可工作的开发环境可能是令人生畏的,尤其是对于绝对的初学者来说。为了跟进本书中的项目,您需要访问 Apache、PHP 和 MySQL 的有效安装,最好是在您的本地机器上。出于速度和安全性的考虑,总是希望在本地进行测试。这样做既保护了你正在进行的工作不受开放互联网的影响,又减少了上传文件到 FTP 服务器和等待页面重新加载所花费的时间。
为什么需要 Apache、MySQL 和 PHP
PHP 是一种强大的脚本语言,可以在任何安装了 PHP 的计算机的命令行中独立运行。然而,单靠 PHP 不足以构建动态网站。要在网站上使用 PHP,您需要一个可以处理 PHP 脚本的服务器。Apache 是一个免费的 web 服务器,一旦安装在计算机上,就允许开发人员在本地测试 PHP 脚本;这使得它成为您的本地开发环境的无价之宝。
此外,用 PHP 开发的网站通常依赖于存储在数据库中的信息,因此可以快速方便地修改这些信息。这是 PHP 站点和 HTML 站点的一个显著区别。这就是关系数据库管理系统(如 MySQL)发挥作用的地方。这本书的例子依赖于 MySQL。我选择这个数据库是因为 PHP 提供了对它的原生支持,还因为 MySQL 是一个免费的开源项目。
Note
最终用户可以免费获得一个开源项目,并附带创建该软件所需的代码。用户可以自由地检查、修改和改进代码,尽管有某些附加条件。开源倡议列出了定义开源软件的十个关键条款。您可以在 www.opensource.org/docs/osd
查看这份名单。
PHP 是一种通用脚本语言,最初是由拉斯马斯·勒德尔夫在 1995 年构思的。Lerdorf 创建 PHP 是为了满足为万维网创建页面时对处理数据的简单方法的需求。
Note
PHP 的诞生源于拉斯马斯·勒德尔夫想要创建一个脚本来记录他的在线简历被访问的次数。由于他创作的剧本广受欢迎,勒多夫继续开发这种语言。随着时间的推移,其他开发人员也加入到他的行列中来开发软件。今天,PHP 是互联网上最流行的脚本语言之一。
PHP 最初代表个人主页,并作为一个免费的开源项目发布。随着时间的推移,这种语言被修改以满足用户的需求。1997 年,PHP 被重新命名为 PHP:超文本预处理器,就像现在所知道的那样。在我写这篇文章的时候,PHP 5.5.7 是当前的稳定版本。许多服务器上仍在使用 PHP 的旧版本。
PHP 如何工作
PHP 一般用作服务器端脚本语言;它特别适合创建动态网页。脚本语言的特点是集成了对数据库接口的支持,如 MySQL,这使它成为构建各种 web 应用的首选,从简单的个人网站到复杂的企业级应用。
页面加载时,浏览器会解析 HTML。浏览器根本无法处理 PHP。PHP 由服务文档的机器处理(这个机器被称为服务器)。在文档被发送到访问者的浏览器之前,文档中的所有 PHP 代码都由服务器处理。因为 PHP 是由服务器处理的,所以它是一种服务器端脚本语言。
使用 PHP,您可以创建动态网页——可以根据条件变化的网页。例如:当我登录到我的脸书帐户,我看到我的内容。当您登录您的脸书帐户时,您可以看到您的内容。我们将加载相同的资源( www.facebook.com
),但是我们将被动态地提供不同的内容。这对于 HTML web 文档来说是不可能的,因为它们是静态的,也就是说它们不能改变。每个用户都会看到完全相同的 HTML 页面。本书的其余部分探索了一些你可以用动态网页实现的事情。
PHP 是一种解释型语言,这是 PHP 程序员的另一大优势。许多编程语言要求在运行文件之前将文件编译成机器代码,这是一个非常耗时的过程。绕过编译的需要意味着您能够更快地编辑和测试代码。
因为 PHP 是服务器端语言,所以运行 PHP 脚本需要服务器。在本地机器上开发 PHP 项目意味着在本地机器上安装一个服务器。本书中的例子依靠 Apache Web 服务器来交付您的网页。
Apache 及其功能
Apache 是 web 上最流行的 Web 服务器软件;它托管了当今几乎一半的网站。Apache 是一个开源项目,可以在几乎所有可用的操作系统上运行。Apache 是一个社区驱动的项目,许多开发人员为它的进展做出了贡献。Apache 的开源根源也意味着该软件可以免费获得,这可能大大有助于 Apache 相对于其竞争对手(包括微软的 IIS 和谷歌的 GWS 等)的压倒性人气。
在 Apache HTTP Server 项目网站( http://httpd.apache.org
)上,Apache HTTP Server 被描述为“为包括 UNIX 和 Windows NT 在内的现代操作系统开发和维护开源 HTTP 服务器的努力。这个项目的目标是提供一个安全、高效、可扩展的服务器,提供与当前 HTTP 标准同步的 HTTP 服务。”
与所有 web 服务器一样,Apache 接受 HTTP 请求并提供 HTTP 响应。万维网是建立在 web 服务器上的,您访问的每个网站都展示了 Web 服务器的功能。我已经提到过,虽然 HTML 可以由 web 浏览器处理,但是服务器端脚本语言(如 PHP)必须由 web 服务器处理。由于 Apache 非常受欢迎,所以在本书中它被用于测试目的。
用 MySQL 存储信息
MySQL 是一个关系数据库管理系统(RDBMS)。本质上,这意味着 MySQL 允许用户在基于表的结构中存储信息,使用行和列来组织不同的数据。还有许多其他的关系数据库管理系统。本书中的例子依靠 MySQL 来存储您将在 PHP 脚本中使用的信息,从博客条目到管理员信息。这种方法有很大的优势,我们将详细探讨。
Note
Blog 是 weblog 的缩写,是个人或企业制作的在线日志。
安装 PHP、Apache 和 MySQL
对于新程序员来说,最大的障碍之一是起步。在编写第一行 PHP 代码之前,您必须下载 Apache 和 PHP,通常还有 MySQL,然后阅读那些充满您可能还不理解的技术术语的安装说明。这种经历会让许多开发人员感到不自信,怀疑他们是否正确安装了所需的软件。
就我自己而言,这个障碍让我几个月都没有学习编程,尽管我非常想超越普通的 ole HTML。在成功运行我的第一个 PHP 命令之前,我不止一次,而是三次尝试在我的本地机器上安装 PHP,但都没有成功。
幸运的是,开发社区已经对新手开发人员的挫折做出了回应,提供了几个选项,无论您是为 Windows、Mac 还是 Linux 机器创建应用,都可以免去设置开发环境的所有痛苦。这些选项包括设置 Apache、MySQL 和 PHP 安装的一体化解决方案。
最常见的一体化解决方案是一个名为 XAMPP ( www.apachefriends.org/en/xampp.html
)的程序,它将 Apache、MySQL、PHP 和其他一些有用的工具整合到一个简单的安装程序中。XAMPP 是免费的,可用于 Windows、Mac 和 Linux。本书假设您将使用它作为您的开发环境。
Note
默认情况下,大多数 Linux 发行版都附带了 LAMP 栈(特定于 Linux 的软件,功能类似于 XAMPP)。默认情况下,某些版本的 Mac OS X 也会安装 PHP 和 Apache。
安装 XAMPP
足够的背景。现在,您可以在开发机器上安装 XAMPP 了。这个过程大约需要五分钟,而且完全无痛。
第一步:下载 XAMPP
你的首要任务是获得一份 XAMPP 软件。前往 XAMPP 网站( www.apachefriends.org/en/xampp.html
)下载最新版本(发布时为 1.8.3)。
步骤 2:打开安装程序,按照说明进行操作
下载 XAMPP 后,找到新下载的安装程序并运行它。你应该会看到一个类似于图 1-1 所示的屏幕。
Note
本书中使用的所有截图都是在运行 Mac OS X 10.6.8 的电脑上拍摄的。如果您使用不同的操作系统,您的安装可能会略有不同。用于 Windows 的 XAMPP 提供了额外的选项,例如安装 Apache、MySQL 和 Filezilla(一种 FTP 服务器)作为服务的能力。这是不必要的,会消耗计算机资源,即使它们没有被使用,所以最好关闭这些服务。此外,Windows 用户应该保留c:\xampp
安装目录,以便更容易地理解本书的示例。
图 1-1。
The introductory screen for the XAMPP installer on Mac OS X
点击下一步按钮进入下一个屏幕(见图 1-2 ),在这里您可以选择要安装的组件。使用默认选择即可。XAMPP 安装程序将引导您完成安装过程。图 1-3 至 1-5 显示了剩余的步骤。
图 1-5。
When you’re ready to install, click Next
图 1-4。
You don’t have to learn more about BitNami at this point
图 1-3。
XAMPP installation directory
图 1-2。
Select components to install
安装需要一两分钟才能完成,此时安装人员会显示最后一个屏幕(见图 1-6 ),确认安装成功。
图 1-6。
Installation is complete
步骤 3:测试 XAMPP 以确保正确安装
到目前为止,您已经使用 XAMPP 向导安装了 Apache、PHP 和 MySQL。下一步是激活 Apache,这样就可以写一些 PHP 了。
打开 XAMPP 控制面板
您可以通过导航到新安装的 XAMPP 文件夹并打开 XAMPP 管理器来激活刚安装的应用(参见图 1-7 )。
Note
打开 XAMPP 控制面板时,可能会提示您输入密码。这对服务本身没有影响,也不应该影响本书所涉及的项目。
在开发机器上激活 Apache、PHP 和 MySQL 就像在 XAMPP 管理器中点击 Apache 旁边的 Start 按钮一样简单。系统可能会提示您确认是否允许服务器在您的计算机上运行,并且可能会要求您输入系统密码。这样做之后,状态应该表明 Apache 正在运行,如图 1-7 所示。
图 1-7。
The XAMPP manager shows that the local Apache Web Server is running Note
在 XAMPP 有一个 FTP(文件传输协议)选项。FTP 提供了一种在网络间移动文件的方法。本书中的例子不需要这个选项,所以没有必要在 XAMPP 控制面板中激活它。前几章甚至不需要 MySQL 数据库。
如果 Apache 没有运行呢?
有时,XAMPP Apache 服务器不运行,即使你试图启动它。最常见的问题是它与使用您电脑上相同端口的其他服务冲突。检查您是否运行了 Skype 或 Messenger 或其他类似的网络服务。完全关闭 Skype,如果你幸运的话,你的 Apache 还能运行。
如果它仍然不运行,你可以求助于互联网。XAMPP 在线社区非常有用,大多数安装问题已经在位于 https://community.apachefriends.org/f/viewforum.php?f=34
的 Apache Friends 论坛中得到解决。您也可以在 http://stackoverflow.com/
转而搜索或询问。
验证 Apache 和 PHP 正在运行
检查 Apache 是否在您的开发机器上正常运行是一件简单的事情。只需打开浏览器,进入以下地址:http://localhost。如果一切正常,你将被重定向到http://localhost/xampp/splash.php
(见图 1-8 )。
图 1-8。
Check in your browser that your Apache Web Server is running
如果这个屏幕加载,您已经成功地在开发机器上安装了 Apache 和 PHP!地址 http://localhost 是您正在使用的当前电脑的别名。使用 XAMPP 时,在浏览器中导航到 http://localhost 会告诉服务器打开 web 根目录。这是包含在 XAMPP 安装目录中的htdocs
文件夹。使用服务器访问本地计算机上的 web 根目录的另一种方法是导航到 IP 地址(分配给连接到计算机网络的任何设备的数字标识符),它充当所有 HTTP 服务器的“主”地址:http://127.0.0.1。
选择 PHP 编辑器
您的开发机器现在正在运行 PHP 编程所需的所有程序。下一步是决定如何编写脚本。PHP 脚本是基于文本的,所以您有无数的选择,从简单的Notepad.exe
和文本编辑程序到高度专业化的集成开发环境(ide)。
大多数有经验的 PHP 开发人员都使用 IDE,因为它有很多好处。许多初学者在使用 IDE 时会有一些困难,也许是因为 IDE 有太多的特性,初学者会感到困惑。
您可能可以使用任何您用来编写 HTML 和 CSS 的程序来编写 PHP 代码。一个好的编辑应该有一些特点。
- 语法突出显示:这是识别编程语言中某些单词的能力,例如变量、控制结构和各种其他特殊文本。这个特殊的文本被突出显示或以其他方式区分,以便更容易地扫描您的代码。
- 内置函数引用:当您输入函数或对象方法的名称时,该特性会显示可用的参数、声明该函数的文件、该函数功能的简短描述以及参数和返回值的更深入的细分。事实证明,在处理大型库时,这个特性是非常宝贵的,它可以省去您查阅 PHP 手册来检查函数的参数顺序或可接受的参数的麻烦。
- 自动完成特性:该特性将可用的 PHP 关键字添加到一个下拉列表中,允许您快速、轻松地从列表中选择想要的关键字,省去了您每次记忆和键入关键字的努力。说到工作效率,每一秒都很重要,这个特性是节省时间的好方法。
- 代码折叠:这个特性允许您折叠代码片段,使您的工作空间整洁,代码易于导航。
- 自动缩进:自动缩进你写的代码,保持一致。这种缩进的代码对于人类读者来说更容易阅读,因为缩进表明了代码块之间的关系。
- 内置 ftp:当你想在万维网上发布你的项目时,你需要 ftp 把你的 PHP 文件上传到一个在线的 web 服务器上。您可以使用独立的 ftp 程序,但是如果它内置在您的 IDE 中,您只需单击一下就可以上传整个项目。
您有许多好的 ide 和编辑器可供选择。NetBeans 和 Eclipse PDT 都是优秀的免费 ide。如果您想习惯专业开发人员经常使用的工具,请尝试一种或两种方法。初学者可能会发现从简单的编辑器开始更容易。我真的很喜欢 Komodo Edit:它和任何编辑器一样容易使用,并且它提供了刚刚列出的开箱即用的大多数功能,包括 PHP 和许多其他语言的优秀自动完成功能。下面是刚才提到的三个 PHP 编辑器的下载链接:
- 从
https://netbeans.org/downloads/
获取 NetBeans。 - 从
http://projects.eclipse.org/projects/tools.pdt
获取 Eclipse PDT。 - 从
www.activestate.com/komodo-edit/downloads
获取科莫多编辑。
我将使用科莫多编辑这本书的例子。使用任何其他编辑器都应该不难理解这些示例。如果您决定使用 ide,您将必须查阅在线文档,以了解如何在您选择的 IDE 中建立新项目。
创建你的第一个 PHP 文件
一切都设置好并正常运行后,是时候冒险编写您的第一个 PHP 脚本了。作为一种服务器端脚本语言,PHP 需要 Apache 之类的 web 服务器才能运行。您已经在本地计算机上安装了 Apache,所以您的系统已经准备好了。
Apache 将解释保存在名为htdocs
的文件夹中的任何 PHP 文件。你可以在XAMPP/xamppfiles/htdocs
的 XAMPP 装置中找到它。
您很快就会创建许多 PHP 文件,所以保持它们的有序是个好主意。在htdocs
里面新建一个文件夹,命名为ch1
。
现在打开 Komodo Edit,或者任何你决定使用的编辑器或 IDE。从科莫多编辑,您可以选择文件➤新➤新文件从主菜单。在新文件中,编写以下内容:
<?php
echo "Hello from PHP";
在 Komodo Edit 中,选择文件➤保存以查看文件保存对话框(图 1-9 )。将新文件另存为test.php
,在htdocs/ch1
中;将格式设置为所有文件;然后单击保存。
图 1-9。
The Save As dialog box from Komodo Edit
运行您的第一个 PHP 脚本
下一步是让 Apache 处理您的 PHP 脚本。如果您通过浏览器请求脚本,这将自动发生。因此,打开一个 web 浏览器,导航到 http://localhost/ch1/test.php,您会惊奇地在浏览器中看到 php 生成的输出(图 1-10 )。您已经成功地创建并执行了您的第一个 PHP 脚本!
图 1-10。
Seeing the output from test.php
in the Chrome web browser
摘要
在这一章中,你学习了一些关于 PHP、MySQL 和 Apache 的知识。您发现了它们是什么,以及它们在动态网站的开发中扮演什么角色。通过安装 XAMPP 和科莫多编辑软件,你还学会了在本地计算机上安装一个功能齐全的开发环境的简单快捷的方法。
在下一章中,您将学习 PHP 的一个小而有效的子集,包括变量、对象和一些本地语言结构和语句。几乎你学到的所有东西都将在你的新开发环境中进行测试,所以保持 XAMPP 的 Apache 服务器开放和运行。
二、理解 PHP 语言基础
到目前为止,您已经绕过了创建开发环境的旧的、繁琐的方法,现在可以开始编写代码了。
但是你从哪里开始呢?在这一章中,我将介绍开始使用 PHP 创建强大的动态 web 应用需要遵循的步骤。你还将开始发展创建博客所需的基本技能。此外,您将学习如何完成几项任务,包括如何执行以下操作:
- 在网页中嵌入 PHP
- 将数据作为输出发送到浏览器
- 在代码中添加注释
- 使用变量
- 处理 PHP 错误
- 创建 HTML5 模板
- 使用对象
- 连接字符串
- 用
$_GET
超级全局变量访问 URL 变量 - 声明类定义
- 嵌入动态 CSS
在这一章结束时,你将会看到一些基本的 PHP 允许你创建、存储、操作和输出数据。您将使用这些技能开发一个个人投资组合网站的基本版本。
Note
本章讨论了 PHP 语言的基本方面,但不是全部细节。为了澄清,更多的例子,或者为了概念强化,访问 PHP 手册的 www.php.net/manual/en/
并在显示“在函数列表中搜索 _ _ _ _ _ _ _ _ _ 的字段中搜索函数或者,您可以通过导航到 http://php.net/function_name
来访问许多 PHP 函数的信息。不要忘记阅读评论,因为你的许多程序员同事在他们的评论中提供了见解、技巧,甚至附加功能。
嵌入 PHP 脚本
在第一章中,当我谈到 Apache 和 web 服务器时,我提到了服务器如何在将文件发送到浏览器之前处理 PHP 文件。但是您可能会好奇服务器是如何知道在哪里寻找 PHP 的。
默认情况下,服务器只在以扩展名.php
结尾的文件中寻找 PHP。但是一个.php
文件可能包含不属于 PHP 脚本的元素,搜索整个文件寻找潜在的脚本是令人困惑和耗费资源的。为了解决这个问题,所有的 PHP 脚本都需要包含 PHP 分隔符。要开始一个 PHP 脚本,您需要包含开始分隔符<?php
并开始编码。最后,只需在脚本末尾添加?>
即可。这些分隔符之外的任何内容都将被视为 HTML 或纯文本。
您可以在实践中看到这一点。首先在/xampp/htdocs/
中创建一个新文件夹ch2
。接下来,用 Komodo Edit 创建一个新文件test.php
。编写以下代码:
<p>Static Text</p>
<?php
echo "<p>This text was generated by PHP!</p>";
?>
<p>This text was not.</p>
保存文件,在浏览器中导航到 http://localhost/ch2/test.php,您应该在浏览器中看到以下输出:
Static Text
This text was generated by PHP!
This text was not.
如您所见,PHP 分隔符内的文本被作为脚本处理,但外部的文本被呈现为常规的 HTML。一个页面中可以包含多少 PHP 代码块没有限制,所以下面的代码片段完全有效:
<?php
echo "<p>This is some text.</p>";
?>
<p>Some of this text is static, <?php echo "but this sure isn't!"; ?></p>
<?php echo "<p>"; ?>
This text is enclosed in paragraph tags that were generated by PHP.
<?php echo "</p>"; ?>
前面的代码片段将以下内容输出到浏览器:
This is some text.
Some of this text is static, but this sure isn't!
This text is enclosed in paragraph tags that were generated by PHP.
如果你写了一个 PHP 脚本,除了 PHP 什么都没有,你不需要结束 PHP 分隔符。如果你要在文件中写一些不是 PHP 的东西,你只需要标记一个 PHP 代码块的结尾。
使用回声
额外看看前面代码示例中echo
的用法。PHP 的echo
是一个所谓的语言构造——PHP 的基本语法单位。echo
语句可能是从 PHP 向浏览器输出文本的最常见方法。这就是echo
所做的一切。它将输出发送到浏览器。
请注意,在前面的代码示例中,输出字符串用双引号分隔。第一个双引号表示字符串的开始。第二个双引号标记要输出的字符串的结尾。在 PHP 中,你必须分隔你在代码中使用的任何字符串。字符串分隔符告诉 PHP 一个字符串何时开始和结束,这是 PHP 需要知道的,以便处理您的代码。
Note
字符串是“文本”的一个怪词因为计算机不是人,它不会真的看到文本,更不会看到文字。他们看到字符串。
什么是变量?
变量是一个关键字或短语,用作存储在系统内存中的值的标识符。这很有用,因为它允许我们编写对变量值执行一组操作的程序,这意味着您可以简单地通过更改变量来更改程序的输出,而不是更改程序本身。
将值存储在变量中
在变量中存储一个值非常简单。在一行中,您可以声明一个新变量并为其赋值:
<?php
$myName = "Thomas";
$friendsName = "Brennan";
echo "<p>I am $myName and I have a friend called $friendsName.</p>";
如果您将前面几行输入到您的test.php
文件中,并将其加载到您的浏览器中,您应该会看到如下输出:
I am Thomas and I have a friend called Brennan.
也许您会注意到,前面的代码只包含 PHP。因此,没有必要用 PHP 分隔符来标记 PHP 代码块的结尾。喜欢的话可以在末尾加?>
;没什么区别。
变量是一个占位符
变量在编程中被广泛使用。这是一个你必须理解的基本概念。从前面的例子中可以学到重要的一课。当您阅读 PHP 代码时,您会看到变量名:
echo "<p>I am $myName and I have a friend called $friendsName.</p>";
您可以在浏览器中看到 PHP 的输出。您可以看到 PHP 用字符串值替换了变量名。比如看到$myName
,PHP 看到的是托马斯。看到$friendsName
,PHP 看到的是布伦南。
变量是特定值的占位符。PHP 甚至不会注意到这个变量;它看到存储在里面的值。打个比方,你可以把一个变量理解为一个容器,比如一个杯子。我的电脑旁边有一个杯子,我可以在里面放各种各样的东西:咖啡、一支铅笔或一些零钱。PHP 变量就是这样。PHP 看到的是包含的内容,而不是容器。
Note
用技术术语来说,PHP 变量是通过值传递的,而不是通过引用传递的。
有效的 PHP 变量名
在 PHP 中,所有变量都必须以美元符号字符($)开头。对于有效的变量名还有一些进一步的限制,但是如果您仅仅使用字母字符,您将不会遇到无效变量名的问题。因此,避免空白字符、数字和特殊字符,如!"#€%&/.
Note
你可以在变量名中使用数字,但不能在初始位置使用。因此,$1a
是一个无效的变量名,而$a1
是完全有效的。
显示 PHP 错误
在学习 PHP 的过程中,您肯定会犯一些错误。当你写了一些错误的 PHP 时,很容易认为你做了坏事。从某种意义上说,这当然是不好的。您可能更喜欢从一开始就编写完美的 PHP。
从另一个意义上说,错误是一件非常好的事情。许多这样的错误提供了一个学习的机会。如果你真正理解了错误的原因,你就不太可能重复它,即使你重复了,如果你理解了,你也可以很容易地纠正错误。
PHP 错误消息并不总是显示出来——这取决于您的开发环境是如何设置的。如果你在脚本的开头写了下面两行 PHP 代码,所有的错误信息都会显示出来。让我们产生一个错误:
<?php
//these two lines tell PHP to show errors in the browser
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
//here comes the error
echo "This string never ends;
你看到错误了吗?只有一个字符串分隔符。要编写有效的 PHP,必须用字符串分隔符将字符串括起来,例如双引号。在前面的例子中,缺少结束分隔符,所以 PHP 看不到输出结束的位置。如果运行该代码,您将在浏览器中看到一条错误消息,如下所示:
Parse error:``syntax error, unexpected $end, expecting T_VARIABLE or T_DOLLAR_OPEN_CURLY_BRACES or T_CURLY_OPEN in``/Applications/XAMPP/xamppfiles/htdocs/ch2/test.php``on line
错误消息是友好的,但并不总是像您希望的那样精确。当 PHP 无法处理您的代码时,就会触发一个错误。PHP 将对问题可能是什么做出有根据的猜测。在前面的例子中,PHP 在第 4 行遇到了“意外结束”。你的剧本里有一个“bug”。请通过添加缺少的双引号来调试脚本。
我建议你养成强制显示错误信息的习惯,并尝试阅读你遇到的所有错误信息。如果你遇到一个你不理解的错误信息,你可以在网上搜索解释。像 www.stackoverflow.com
这样的网站很可能会对你的错误信息给出解释。
用 PHP 创建 HTML5 页面
PHP 是创建动态 HTML 页面的绝佳语言。只需一点点 PHP,您就可以创建一个有效的 HTML5 页面,在内存中包含可变内容,并让 PHP 将创建的页面输出到浏览器。让我们为个人投资组合网站制作一个基本框架。在XAMPP/htdocs/ch2
中创建一个名为index.php
的新 PHP 文件:
<?php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
$title = "Test title";
$content = "<h1>Hello World</h1>";
$page = "
<!DOCTYPE html>
<html>
<head>
<title>$title</title>
<meta http-equiv='Content-Type' content='text/html;charset=utf-8'/>
</head>
<body>
$content
</body>
</html>";
echo $page;
如果您在浏览器中保存并加载 http://localhost/ch2/index.php,您应该会看到一个格式良好的 HTML5 页面,带有一个标题和一个标题。检查 PHP 生成的 HTML 页面的源代码是一个好习惯。这样做,您应该会看到变量已经被 PHP 替换为相应的值。HTML 源代码应该如下所示:
<!DOCTYPE html>
<html>
<head>
<title>Test title</title>
<meta http-equiv='Content-Type' content='text/html;charset=utf-8' />
</head>
<body>
Hello World</h1>
</body>
</html>
包括一个简单的页面模板
用 PHP 创建一个有效的 HTML5 页面是一个非常非常普通的任务。理解前面的代码应该没什么问题。让我们尝试以一种更容易在其他项目中重用的方式创建相同的输出。如果您可以在其他项目中重用您的代码,您就可以更快、更有效地开发解决方案。让我们将 HTML5 页面模板保存在一个单独的文件中。
在现有的 PHP 项目中创建一个名为templates
的新文件夹。在 templates 文件夹中创建一个名为page.php
的新 PHP 文件,如下所示:
<?php
return "<!DOCTYPE html>
<html>
<head>
<title>$title</title>
<meta http-equiv='Content-Type' content='text/html;charset=utf-8'/>
</head>
<body>
$content
</body>
</html>";
返回值
PHP 中的return
语句非常有用。它只是停止脚本的执行。将返回紧跟在return
语句之后的任何值。在前面的示例中,将返回一个有效的 HTML5 页面。
包括模板
要使用索引中的模板,您必须将脚本加载到 PHP 的内存中。您可以用另一个 PHP 语句来做这件事:include_once
。更新您的index.php
文件,如下所示:
<?php
//complete code for index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
$title = "Test title";
$content = "<h1>Hello World</h1>";
//indicate the relative path to the file to include
$page = include_once "templates/page.php";
echo $page;
上述代码的输出将与您第一次创建页面时的输出相同。没有功能上的变化,但是在代码架构上有一些美学上的变化。可重用的页面模板现在保存在一个单独的文件中。需要时,模板包含在index.php
中。我们实际上是将代码的不同部分拆分到不同的文件中。结果是更多的代码可以在其他项目中重用。这个分离不同部分的过程也称为关注点分离。
注释您的代码
在代码中写注释对你的学习过程很有帮助。这样的注释应该提醒你代码做了什么以及为什么。用你自己的语言解释代码会加快你的学习过程。此外,如果您发现自己与一组开发人员一起工作,代码注释将帮助您有效地协作,因为您可以将注释作为代码注释写给共同开发人员。
块注释和单行注释
您可以在代码中编写注释。注释可以提醒你和其他阅读你的代码的人,代码的不同部分是做什么的。您必须清楚地界定注释,这样 PHP 就不会试图将注释解释为实际的产品代码。你应该知道用 PHP 编写代码注释的两种方式:块注释和单行注释。
<?php
//this is a single-line comment
/*
This is a comment block
It may span across
several lines
*/
避免命名冲突
你很快就会发现自己用数百行代码编写 PHP 项目。您将需要许多变量,每个变量都必须有唯一且有意义的名称。您必须避免命名冲突,如下例所示:
<?php
$title = "Welcome to my blog";
/*
hundreds lines of code later
*/
$title = "Web developer";
看到问题了吗?最初,名为$title
的变量用于指示 HTML 页面的<title>
元素的值。很久以后,在同一个系统中,一个名为$title
的变量被用来存储职位名称。具有这种变量名的系统易受攻击。使用该变量时,您可能会看到不需要的系统行为。更好的解决方案是清楚地指出$title
的上下文。一种方法是使用对象。
<?php
$pageData = new StdClass();
$pageData->title = "Welcome to my blog";
/*
hundreds lines of code later
*/
$jobData = new StdClass();
$jobData->title = "Web developer";
您可以通过使用 PHP 的 native StdClass
创建一个新的标准 PHP 对象。PHP 对象就像一个变量,它可以存储值。一个普通的 PHP 变量可以存储一个值。一个对象可以存储任意多的值。每个单独的值都可以存储为唯一的对象特性。
在前面的代码示例中,您可以看到两个不同的对象,每个对象都有一个 title 属性。应该清楚的是,$pageData->title
不同于$jobData->title
,即使两个属性都被命名为title
。
该对象提供了一个上下文,这将使您更容易在代码中的正确位置使用正确的标题。您可以使用对象将代码组织成有意义的单元。你可以说一个对象和它的属性很像一个文件夹和里面的文件。
Note
对象的意义远不止于此——远不止于此。在代码中使用对象是处理系统复杂性的事实标准,不会在代码中引入不必要的复杂性。在整本书中,你会学到更多关于用对象编程的知识。
对象运算符
通过提供清晰的上下文,对象可以用作属性的命名空间,以避免命名冲突。要从对象属性中获取值,您必须指定两件事:要获取哪个对象及其属性。为此,您使用 PHP 的对象操作符。一般语法如下所示:
$objectName->propertyName;
PHP 的对象操作符看起来像一个箭头。它表示您正在获取特定对象中的特定属性。
对页面数据使用 StdClass 对象
让我们用一个对象重构index.php
和页面模板,以防止烦人的命名冲突。以下是index.php
的一些变化:
<?php
//complete code for index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
$pageData = new stdClass();
$pageData->title = "New, object-oriented test title";
$pageData->content = "<h1>Hello from an object</h1>";
$page = include_once "templates/page.php";
echo $page;
您还必须更新templates/page.php
,以便它在正确的位置使用新创建的对象及其属性:
<?php
return "<!DOCTYPE html>
<html>
<head>
<title>``$pageData->title
<meta http-equiv='Content-Type' content='text/html;charset=utf-8' />
</head>
<body>
$pageData->content
</body>
</html>";
保存文件并在浏览器中重新加载index.php
。期望在<title>
和<body>
元素中看到改变的值。
对象属性就像变量一样
普通 PHP 变量是值的简单占位符。对象显然更复杂,因为一个对象可以保存几个值。前面的StdClass
对象保存两个独立的值。每个值都存储在唯一的对象特性中。对象属性的行为类似于 PHP 变量。它们是值的简单占位符。一个对象可以有许多属性。每个属性可以包含一个值。在前面的示例中,您可以看到,为了获取值,您必须同时指定对象和属性。
PHP 属性可以理解为 cups。它们所包含的价值可以理解为杯子里的咖啡(或任何东西)。打个比方,你可以把一个对象看作一个托盘,把它的属性看作托盘上的几个杯子。要得到你的咖啡,你必须从正确的托盘中得到正确的杯子。
在前面的代码示例中,您可以看到在<title>
中使用了$pagedata
对象的title
属性,在<body>
元素中使用了$pageData
对象的content
属性。
页面视图
一个个人作品集网站可能会有几个不同的页面。也许一页是关于你的技能和教育背景,另一页是你工作实例的链接。
因为您正在创建一个动态网站,所以您不必创建两个完整的 HTML 页面。您可以使用页面模板显示两种不同的页面视图。页面视图是看起来像单个页面的东西。一个页面视图可能由几个较小的视图组成。你可以把一个页面视图想象成一个乐高房子,把一个视图想象成一个乐高积木:把较小的部分组合起来,就可以建造出更大的东西。
让我们将所有视图保存在一个文件夹中。在现有的项目文件夹中创建一个名为views
的新文件夹。创建一个新文件,views/skills.php
。
<?php
return "<h1>Skills and educational background</h1>
<p>Read all about my skills and my formal training</p>
";
这是完整的文件。这是一个非常小的观点。开发代码时,从小处着手通常是个好主意。任何潜在的错误都将更容易在更少的代码行中被发现。您需要在views/projects.php
中查看另一个小视图。
<?php
return "<h1>Projects I have worked on</h1>
<ul>
<li>Ahem, this will soon be updated</li>
</ul>";
制作动态网站导航
你必须在正确的时间展示正确的观点。您可以创建一个全局的、持久的站点导航,即,在网站的每个页面上都是相同的导航。因为 PHP 可以包含文件,所以您可以简单地将导航代码保存在一个文件中,并在每个需要它的脚本中包含它。一个明显的优点是你可以在一个文件中改变导航,这种改变会反映在每个站点页面上,不管有多少个页面。在views/navigation.php
中创建一个新文件。
<?php
return "
<nav>
<a href='index.php?page=skills'>My skills and background</a>
<a href='index.php?page=projects'>Some projects</a>
</nav>
";
注意,整个导航字符串用双引号分隔。使用单引号来分隔href
属性值,因此,不能在导航字符串中使用双引号。第三个双引号将触发 PHP 错误。所以你。
要查看索引页面上的导航,您必须从index.php
开始包含它。
<?php
//complete code for index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
$pageData = new stdClass();
//changes begin here
$pageData->title = "Thomas Blom Hansen: Portfolio site";
$pageData->content = include_once "views/navigation.php";
//end of changes
$page = include_once "templates/page.php";
echo $page;
保存并运行这段代码,您应该会看到一个带有导航的页面。现在还不要期望看到任何视图。
用 PHP 传递信息
传递数据是区分动态网页和静态网页的关键。通过根据用户的选择定制体验,您可以为网站增加全新的价值。可以通过 URL 变量将信息传递给 PHP。URL 变量就是在 URL 中声明的变量。您可以在导航中看到两个 URL 变量。仔细看看导航<a>
元素中的href
属性。
index.php?page=skills
index.php?page=projects
href
表示点击导航项将加载index.php
并编码一个名为page
的 URL 变量。如果你点击一个链接,名为page
的 URL 变量将得到一个值skills
。如果点击另一个链接,page
会得到一个值projects
。
PHP 可以访问 URL 变量并使用它们,例如,在正确的时间加载正确的页面视图。URL 变量是动态网站的生命线。
访问 URL 变量
要访问 URL 变量,可以使用$_GET
超全局数组。以下是您在index.php
中如何使用它:
<?php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
$pageData = new stdClass();
$pageData->title = "Thomas Blom Hansen: Portfolio site";
$pageData->content = include_once "views/navigation.php";
//changes begin here
$navigationIsClicked = isset($_GET['page']);
if ($navigationIsClicked ) {
$fileToLoad = $_GET['page'];
$pageData->content .= "<p>Will soon load $fileToLoad.php</p>";
}
//end of changes
$page = include_once "templates/page.php";
echo $page;
那是相当多的一口!PHP 可以通过$_GET
访问 URL 变量。要访问名为page
的 URL 变量的值,您需要编写$_GET['page']
。只有当用户点击了一个导航项目时,才会有一个名为page
的 URL 变量。
使用 isset()测试变量是否已设置
如果你试图使用一个不存在的变量,你将触发一个 PHP 错误。所以,在你试图访问一个变量之前,你必须确保这个变量已经设置好了。PHP 为此构建了一种语言。您已经看到了它的作用。
$navigationIsClicked = isset($_GET['page']);
如果括号内的变量被设置,isset()
函数将返回TRUE
。所以,如果用户点击了一个导航项,$navigationIsClicked
将会是TRUE
;如果不是,那就是FALSE
。
如果$navigationIsClicked
是TRUE
,那么声明一个名为$fileToLoad
的 PHP 变量,来存储名为page
的 URL 变量的值。接下来,向$pageData->content
属性添加一个字符串,以显示名为page
的 URL 变量的值。保存并运行代码。在浏览器中加载后,单击“我的技能”导航项目。这应该会在您的浏览器中产生以下输出:
Will soon load skills.php
如果您单击另一个导航项目,您可以看到输出的变化。您会看到,根据用户与站点的交互方式,输出会动态变化。
$_GET,一个超全局数组
PHP 可以通过一个叫做$_GET
的所谓超全局数组来访问 URL 变量。PHP 还有一些用于其他目的的超全局数组。使用$_GET
,您可以通过名称访问 URL 变量。在导航中,你有两个<a>
元素。单击其中任何一个都会为一个名为page
的 URL 变量编码一个唯一的值。
在图 2-1 中可以看到浏览器地址栏中的一个 URL 变量。注意 URL 变量page
的值在输出中是如何表示的?要用 PHP 获取 URL 变量的值,您需要编写
$_GET['the name of the url variable'];
图 2-1。
A URL variable in action
动态包含页面视图
动态站点导航即将完成。它工作得很好,除了当导航项目被点击时页面视图没有被加载。让我们通过更新index.php
中的代码来改变这一点,如下所示:
<?php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
$pageData = new stdClass();
$pageData->title = "Thomas Blom Hansen: Portfolio site";
$pageData->content = include_once "views/navigation.php";
$navigationIsClicked = isset($_GET['page']);
if ($navigationIsClicked ) {
$fileToLoad = $_GET['page'];
//change one line to load page views dynamically
$pageData->content .=include_once "views/$fileToLoad.php";
}
$page = include_once "templates/page.php";
echo $page;
保存更改并在浏览器中重新加载index.php
。当您单击一个导航项目时,您会看到输出发生了变化。将加载并显示相应的页面视图。是您的index.php
文件动态地改变了它的外观。名为page
的 URL 变量将决定加载哪个文件。
就是这样!这是一个基本的,动态的网站,具有持久的,全球性的导航。
串联
你注意到上面代码中的.=
了吗?它是 PHP 的增量连接运算符,和基本赋值运算符=
有一点不同。
这里有一个例子来说明不同之处:
<?php
$test = "<p>Hello ";
$test = "world</p>";
echo $test;
$concatenationTest = "<p>Hello ";
$concatenationTest .= "world</p>";
echo $concatenationTest;
上例的 HTML 源代码输出清楚地显示了赋值和增量连接之间的区别。
world</p>
<p>Hello world</p>
赋值操作符为变量赋值一个新的字符串值,并覆盖该过程中任何以前的字符串。因此,变量$test
中的初始"<p>Hello "
被覆盖。
增量连接操作符将$concatenationTest
中的现有字符串与一个新字符串合并。增量串联在现有字符串的末尾添加新字符串。
严格的命名约定
很高兴看到你的第一个动态网站工作,不是吗?它是可行的,并且依赖于严格的命名约定。导航项目为名为page
的 URL 变量编码不同的值。相应的页面视图文件必须同名,并保存在views
文件夹中。
超链接 | 可变 URL | 视图文件 |
---|---|---|
index.php?page =技能 | page =技能 | view/skills . PHP |
index.php?页面=项目 | 页面=项目 | views/projects.php |
显示默认页面
动态导航非常好用,但是它有一个缺点:当用户导航到index.php
时,没有显示默认的页面视图,在这种情况下,名为page
的 URL 变量没有值。在index.php
很容易改变。您只需稍微改变一下if
语句。
//partial code for index.php
if ($navigationIsClicked ) {
$fileToLoad = $_GET['page'];
} else {
$fileToLoad = "skills";
}
$pageData->content .=include_once "views/$fileToLoad.php";
显著的变化是$fileToLoad
从 URL 变量page
中获取它的值,如果设置了的话。如果没有设置,$fileToLoad
将有一个默认值skills
。一旦$fileToLoad
有了值,你就可以用它来加载用户请求的页面视图或者关于“我的技能”的默认页面视图
验证您的 HTML
生成 HTML 页面的过程有点抽象。如果在正确的时间显示正确的页面视图,很容易认为一切都是完美的。如果你看到了正确的动作,你的 PHP 脚本就会完美地工作。但这并不意味着你的 HTML 是完全有效的。动态网页应该符合 web 标准,就像静态 HTML 页面一样。您应该像验证任何其他 HTML 一样验证生成的 HTML。
Note
您可以在浏览器中加载一个动态页面,并通过浏览器查看生成的 HTML 源代码。当您看到生成的 HTML 源代码时,您可以选择它,复制它,并将其粘贴到在线 HTML 验证服务中。我一般用 http://validator.w3.org/#validate_by_input
。
用 CSS 设计网站样式
当所有页面视图的 HTML 都有效时,你就可以开始用 CSS 来设计你的站点了。您完全可以像通常样式化静态 HTML 站点那样做:为站点的视觉设计创建一个带有样式规则的外部样式表。为了对 portfolio 站点进行这样的操作,您可以在您的项目文件夹中创建一个名为css
的新文件夹。在css
文件夹中创建一个名为layout.css
的新文件:
nav {
background-color: #CCCCDE;
padding-top: 10px;
}
nav a{
display:inline-block;
text-decoration:none;
color: #000;
margin-left: 10px;
}
nav a:hover{text-decoration: underline;}
您可以更改或添加您喜欢的任何样式规则。前面的css
只是让你开始。您可能想要设计所有动态 HTML 页面的样式,那么为什么不将这个功能构建到页面模板中呢?您只需为指向外部样式表的<link>
元素添加一个新的占位符。让我们更新templates/page.php
:
<?php
return "<!DOCTYPE html>
<html>
<head>
<title>$pageData->title</title>
<meta http-equiv='Content-Type' content='text/html;charset=utf-8' />
$pageData->css
</head>
<body>
$pageData->content
</body>
</html>";
注意,新属性被用作引用外部样式表的<link>
元素的占位符。要使用更新的页面模板,您必须更新index.php
并为新属性声明一个值:
<?php
//partial code listing for index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
$pageData = new stdClass();
$pageData->title = "Thomas Blom Hansen: Portfolio site";
$pageData->content = include_once "views/navigation.php";
//one line of code added here
$pageData->css = "<link href='css/layout.css' rel='stylesheet' />";
保存文件并在浏览器中加载index.php
。期待看到你的风格规则生效。
声明 Page_Data 类
有时,使用内部的嵌入式样式表来补充外部样式表会非常有用。您可以很容易地用一个<style>
元素的占位符来更新页面模板。更新templates/page.php
。
<?php
return "<!DOCTYPE html>
<html>
<head>
<title>$pageData->title</title>
<meta http-equiv='Content-Type' content='text/html;charset=utf-8' />
$pageData->css
$pageData->embeddedStyle
</head>
<body>
$pageData->content
</body>
</html>";
从index.php
声明一个属性值同样容易,但是让我们做一些不同的事情。问题是有时你不需要任何嵌入的<style>
元素,而有时你需要。
既然您的模板有了一个用于嵌入 CSS 的占位符,那么该属性必须始终有一个值。你不想浪费时间为一个多余的<style>
元素声明一个值,所以让我们做一个更智能的解决方案。让我们朝着面向对象编程迈出下一步,为页面数据创建一个自定义类。在您的项目文件夹中创建一个名为classes
的新文件夹。在classes
文件夹中创建一个名为Page_Data.class.php
的新文件。
<?php
class Page_Data {
public $title = "";
public $content = "";
public $css = "";
public $embeddedStyle = "";
}
就是这样—一个定制的类,为页面模板所需的属性预定义了空字符串值。关键字class
表示后面的名称是自定义类名。
PHP 类名可以以字母或下划线开头。习惯上以大写字母开头。如果类名是一个复合词,通常用下划线字符分隔各个单词,并以大写字母开始下一个单词,例如My_Custom_Class_Name
。
我通常将我的每个自定义 PHP 类定义保存在一个单独的文件中,其名称类似于类名。我也喜欢用可选的后缀.class.php
来结束文件名。因此,My_Custom_Class_Name
的文件将被称为My_Custom_Class_Name.class.php
。
在类名后面是一组花括号,用来为类定义划分代码块。看一下Page_Data
的代码块。在花括号内,使用关键字public
声明了四个属性。结果是Page_Data
类将会有四个默认属性,每个属性都声明了一个默认值。
类制造对象
您可以使用来自index.php
的新类定义。这将是一个微小的变化。更新index.php
如下:
//Partial code listing for index.php
include_once "classes/Page_Data.class.php";
$pageData = new Page_Data();
//delete or comment out the previous object
//$pageData = new stdClass();
//no changes below this point
在浏览器中加载 http://localhost/ch2/index.php,以测试您的代码。您的网站应该完全像以前一样工作。你知道你对代码做了一些改动,但是这些改动普通用户是看不到的。您已经重构了代码。
要使用自定义的类,必须首先包含类定义。接下来,您必须使用new
关键字用类定义创建一个新对象。Page_Data
类使我们能够在页面模板中为嵌入的样式保留一个占位符,并且只要您需要一个带有嵌入的<style>
元素的页面,就只为该属性分配一个实际值。
用动态样式规则突出显示当前导航项目
您有一个页面模板和一个Page_Data
对象,它们准备好处理嵌入的样式。您通常希望将样式规则保存在外部样式表中。对于动态网站,这个约定仍然适用,但是因为您可以将样式嵌入到index.php
中,所以您可以很容易地使用动态样式。大多数时候,好的 ole 外部样式表可以很好地完成工作。但是在少数情况下,动态样式非常强大。您可以使用动态样式规则来突出显示当前导航项目。这真的很简单,一旦你明白了。更新index.php
如下:
<?php
//complete code listing for index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
include_once "classes/Page_Data.class.php";
$pageData = new Page_Data();
$pageData->title = "Thomas Blom Hansen: Portfolio site";
$pageData->content = include_once "views/navigation.php";
$pageData->css = "<link href='css/layout.css' rel='stylesheet' />";
$navigationIsClicked = isset($_GET['page']);
if ($navigationIsClicked ) {
$fileToLoad = $_GET['page'];
} else {
$fileToLoad = "skills";
}
$pageData->content .=include_once "views/$fileToLoad.php";
//new code below: dynamic style added below
$pageData->embeddedStyle = "
<style>
nav a[href *= '?page=$fileToLoad']{
padding:3px;
background-color:white;
border-top-left-radius:3px;
border-top-right-radius:3px;
}
</style>";
$page = include_once "templates/page.php";
echo $page;
保存您的工作并将索引加载到浏览器中。您应该会看到一个简单的选项卡式导航,当前导航项目清晰地突出显示。在下面的例子中,我点击了“一些项目”导航项。由于突出显示的导航选项卡,用户可以清楚地看到显示的是哪个页面。你可以在图 2-2 中看到我的例子。
你懂一些 PHP,所以你知道这一切的发生是因为名为page
的 URL 变量有一个值skills
。它可能还不是一个很好的视觉设计,但是它应该足够让你应用你所有的 CSS 技能来设计一个漂亮的用户界面。
图 2-2。
Current navigation item highlighted with a dynamic style rule
仔细看看动态 CSS
所使用的 CSS 属性选择器并不常用。让我们仔细看看。
nav a[href *= '?page=$fileToLoad']
首先,注意 PHP 变量$fileToLoad
。它是实际值的占位符。例如,当用户点击“一些项目”导航项时,$fileToLoad
的值为projects
,因为名为page
的 URL 变量的值为projects
。你可以在上图浏览器截图的地址栏看到。因此,当浏览器解释 CSS 时,它实际上会看到以下内容:
nav a[href *= '?page=projects']
选择器告诉浏览器类似“寻找一个<nav>
元素,在这个元素中你会找到一个<a>
元素,它的href
属性包含字符串?page=projects
假装你是浏览器。看看你的<nav>
元素。寻找一个具有包含?page=projects
的href
属性的<a>
元素。
您和浏览器将只找到一个这样的<a>
元素。浏览器将对这个<a>
元素应用一个特殊的样式规则,这个规则将突出显示这个元素。
摘要
现在您已经看到了如何使用一点基本的 PHP 来构建一个非常动态的站点。在这一点上,你的学习过程可能会受益于一些实验。
你可以尝试建立一个个人作品集网站。添加您认为合适的页面视图,并相应地更新您的导航。
你可以尝试创建一些更全面的页面视图。在这个过程中,你会逐渐适应动态站点结构,页面视图会返回到index.php
上显示。
你可以利用你现有的 CSS 技能为你的作品集开发一个一致的网站设计。在这个动态网站的新环境中使用你现有的 HTML 和 CSS 技能将是一个非常好的练习。这对你来说可能相对容易,因为作品集网站相当简单。在你工作的网站很简单的时候做这个练习是个好主意。你开发的动态网站很快就不再简单了。
当你觉得准备好了,翻页学习 HTML 表单、PHP 函数和条件语句,所有这些你都会在第三章中遇到。
三、表单管理
在第二章,我们建立了一个动态的个人作品集网站。在这个过程中,您看到了如何用<a>
元素对 URL 变量进行编码,以及如何使用$_GET
超全局变量来访问这些 URL 变量。传递数据是区分动态网页和静态网页的关键。通过根据用户的选择定制体验,您可以为网站增加全新的价值。
现在,您已经了解了一些 PHP 并编写了一个基本的动态站点,您已经准备好深入研究 URL 变量了。HTML <form>
元素通常用于创建允许用户与动态站点交互的界面。你必须学会如何处理这样的 HTML 表单。在本章中,您将学习以下内容:
- 什么是 HTML 表单以及如何创建它们
- 什么是超全局数组以及如何使用它们
- 如何使用
GET
方法在 HTML 表单中编码 URL 变量 - 如何使用
POST
方法在 HTML 表单中编码 URL 变量 - 如何编写一个动态 PHP 测试
- 何时使用
if-else
条件语句 - 什么是命名函数以及如何编写一个命名函数
- 一部美国西部电影能教会你什么是干净的代码
- 为什么代码真的是诗歌
什么是表格?
HTML 表单允许访问者与站点进行交互。图 3-1 显示了谷歌的搜索表单。当用户访问 www.google.com
,在文本输入字段中键入搜索词,并点击谷歌搜索时,谷歌执行搜索。
图 3-1。
Search form from www.google.com
你一定遇到过的另一种表单是登录表单,注册用户可以通过它登录并进入受限区域。当您登录您的脸书帐户、银行帐户或 Gmail 帐户时,您可能会看到此类表单。图 3-2 中的登录名来自脸书。
图 3-2。
Login form from www.facebook.com
最后一个熟悉的例子是星级评定系统。如果你在网上书店买过一本书,你可能会遇到一个星级系统。图 3-3 显示了亚马逊的星级评定表。
图 3-3。
Star rating form from www.amazon.com
如果你打算从事网页开发或网页设计,你肯定会从事开发和设计有用的、功能性的表单。因为 web 表单是系统和用户之间的接口,所以开发和设计 web 表单是非常重要的。
建立一个新的 PHP 项目
学习需要重复,所以让我们重复上一章学到的一些东西。在XAMPP/htdocs
文件夹中创建一个名为ch3
的新项目文件夹。在ch3
中,你需要来自之前项目的templates
和classes
文件夹的副本,以及里面的 PHP 脚本。创建一个名为views
的空文件夹。打开 Komodo Edit 并在ch3
中创建新的index.php
文件。请确保在保存文件时将格式设置为所有文件。图 3-4 说明了如何操作。
图 3-4。
Save a new file as index.php
with Komodo Edit
你需要在index.php
中安装一些 PHP 来做更多的事情。您可以从输出一个简单的 HTML 页面开始。注意,您应该重用classes/Page_Data.class.php
和templates/page.php
,而不需要修改任何一个脚本中的一行代码。当你知道了 PHP,你就不用多次解决同一个任务了。只需解决一次,并对其进行编码以便重用,如下所示:
<?php
//code listing for index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
include_once "classes/Page_Data.class.php";
$pageData = new Page_Data();
$pageData->title = "Building and processing HTML forms with PHP";
$pageData->content = "<nav>will soon show a navigation...</nav>";
$pageData->content .= "<div>...and a form here</div>";
$page = include_once "templates/page.php";
echo $page;
亲眼看看
为了检查您输入的内容是否正确,您可以保存index.php
并在浏览器中导航到 http://localhost/ch3/index.php。
will soon show a navigation...
...and a form here
没有禅师会拿棍子戳你,但我有几个问题要问你。你的答案将表明你到目前为止学到了什么。如有疑问,可查阅第二章进行解释。
include_once
是做什么的?$pageData->title
如何改变生成的 HTML 页面的<title>
?.=
是什么意思?它的专业名称是什么?- 当我们
echo $page
的时候会发生什么?
创建动态导航
您将创建两个不同的表单。您将需要一个站点导航来在这些表单之间导航。创建一个新文件ch3/views/navigation.php
,如下所示:
<?php
//code listing for views/navigation.php
return "
<nav>
<a href='index.php?page=search'>Search on bing</a>
<a href='index.php?page=quiz'>Dynamic quiz</a>
</nav>
";
就像在第二章中一样,你创建一个 PHP 脚本,简单地返回一小段 HTML 代码。在index.php
中,您将使用一些 PHP 将一些小的 HTML 片段拼接在一起,生成一个格式良好的动态 HTML 页面。以下,index.php
更新显示导航:
<?php
//code listing for index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
include_once "classes/Page_Data.class.php";
$pageData = new Page_Data();
$pageData->title = "Building and processing HTML forms with PHP";
//change this one line below only
$pageData->content = include_once "views/navigation.php";
$pageData->content .= "<div>...and a form here</div>";
$page = include_once "templates/page.php";
echo $page;
为表单创建页面视图
你可以遵循第二章中页面视图的命名约定,因为它似乎为动态网站提供了一个可靠的代码架构。这种组织和命名页面视图的方式可以给你一个构建动态站点的心理框架。当你有了内在的框架,你就会知道你需要哪些文件来开发你想要开发的网站。你不必每次创建一个新的站点都要重新发明一个好的动态代码架构。
上一节描述的导航包含指向名为“搜索”和“测验”的页面的链接。因此,我们必须在views
文件夹中创建两个新的 PHP 文件。
超链接 | 可变 url | 视图文件 |
---|---|---|
index.php?页面=搜索 | 页面=搜索 | views/search.php |
index.php?page =测验 | page=测验 | views/quick . PHP |
使用 Komodo Edit 创建两个新文件,如下所示:
<?php
//code listing for views/search.php
return "will soon show the search form";
<?php
//code listing for views/quiz.php
return "quiz will go here";
显示 index.php 的页面浏览量
为了让index.php
在被请求时显示这些页面视图,您必须额外编写几行代码,这些代码几乎与您在index.php
中为上一个项目编写的代码相同,如下所示:
<?php
//code listing for ch3/index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
include_once "classes/Page_Data.class.php";
$pageData = new Page_Data();
$pageData->title = "Building and processing HTML forms with PHP";
$pageData->content = include_once "views/navigation.php";
//changes begin here
//$pageData->content .= "<div>...and a form here</div>";
$navigationIsClicked = isset($_GET['page']);
if ( $navigationIsClicked ) {
$fileToLoad = $_GET['page'];
} else {
$fileToLoad = "search";
}
$pageData->content .=include_once "views/$fileToLoad.php";
//no changes below
$page = include_once "templates/page.php";
echo $page;
这段代码实际上是告诉 PHP 加载站点访问者请求的任何视图。如果没有点击导航项目,我们将显示views/search.php
。您可以通过在浏览器中加载 http://localhost/ch3/index.php 来测试您的代码是否工作。
明智地使用时间:惯例和重用
你肯定已经注意到这个动态网站非常像第二章中的动态网站。我们重用了一些文件,比如Page_Data
类和页面模板。我们不能完全像在第二章中一样重用index.php
或navigation.php
,但是这个项目是按照相同的惯例构建的。
重用代码是一个好主意,因为这允许您更快地开发解决方案。如果您有在一个项目中工作的脚本,您可以非常信任它们在其他项目中做同样的事情。因此,代码重用减少了调试时间。
总会有你不容易重用的部分,比如导航。但是如果你养成了在不同的项目中以相同的方式创建动态导航的习惯,你将能够快速而轻松地开发新的动态导航。因此,当您不能按原样重用代码时,也许您可以重用那些支持您知道有效的代码的原则。
一个超级简单的搜索表单
HTML 表单是用<form>
元素创建的。还有许多专门为表单设计的 HTML 元素。也许最重要的是<input>
元素。让我们在views/search.php
中创建一个例子,如下:
<?php
return "
<form method='get' action='
http://www.bing.com/search
<input type='text' name='q' />
<input type='submit' value='search on bing' />
</form>
";
尝试您的搜索表单
保存您的工作,将浏览器指向 http://localhost/ch3/index.php 以查看表单。你应该会看到类似图 3-5 的东西。
图 3-5。
A simple search form completely unstyled
您可以在文本字段中键入一些搜索词,然后单击按钮。你的浏览器会加载bing.com
,Bing 会搜索你输入的任何内容。我输入了猫。完成搜索后,查看一下浏览器的地址栏。你会发现类似 http://www.bing.com/search?q=cats
的东西。
Tip
一个
就像一个当您单击提交按钮时,您的浏览器会请求一个新的 URL。这就像点击一个<a>
元素。当您单击一个<a>
元素时,您的浏览器将请求由其href
属性指示的资源。当您提交表单时,您的浏览器将请求由<form>
元素的action
属性指示的资源。
表单编码 URL 变量
<form>
元素的action
属性是 http://www.bing.com/search
,但是当您提交表单时,您的浏览器请求了 http://www.bing.com/search?q=cats
。不知何故,表单将一个名为q
的 URL 变量编码到请求的 URL 中,并将其值设置为cats
。这就是表单可以做的:它们可以在 HTTP 请求中编码 URL 变量。
正如你在前一章看到的,URL 变量可以用 PHP 访问。URL 变量对于动态网站来说是必不可少的。显然,如果设置了名为q
的 URL 变量, www.bing.com
将执行搜索。Bing 将寻找q
拥有的任何价值。
Name 属性声明 URL 变量的名称
理解 URL 变量q
是如何声明的很重要。理解了这一点,你就理解了形式的本质!
URL 变量q
之所以得名,是因为<form>
有一个<input>
元素,其name
属性设置为 q。如果<input>
元素嵌套在<form>
中,你可以推断出具有name
属性的<input>
元素将声明一个 URL 变量。这条规则也适用于表单中常用的其他 HTML 元素的一小部分。在本书中,你会看到更多这样的与表单相关的元素。
元素和一些常见的类型
您是否注意到<input type='text' />
显示为单行文本字段,而<input type='submit'/>
显示为提交按钮?输入type
属性有许多可能的值。在这本书里,你会看到一些输入类型。一旦你能使用这些,学习如何使用剩下的输入类型应该没有问题。
Note
HTML5 引入了不少新的<input>
类型,但并不是所有主流浏览器都实现了这些类型。这些新类型中有许多非常有用。在 http://caniuse.com/#search=input
看哪些浏览器实现了哪些功能。
了解方法属性
到目前为止,您只看到了可以在浏览器地址栏的 URL 中看到的 URL 变量。这种 URL 变量使用 HTTP 方法GET
进行编码。您已经使用这些变量创建了一个动态导航和一个可以在 www.bing.com
执行搜索的表单。
任何用GET
编码的 URL 变量都被限制为相对较少的字符。具体数字因浏览器而异,但有效的最大值似乎是 2000 个字符左右。因为 URL 中的变量是显而易见的,页面可以被书签标记和链接。因此,GET
变量非常适合站点导航。
命名 PHP 函数
也许 PHP 最强大的特性之一是能够在代码中定义和执行函数。函数是在脚本中声明的命名代码块,您可以在以后调用它。您将很快使用函数编写一个动态测验,但是让我们先来看看 PHP 中命名函数的基础知识:
function functionName () {
//function body
}
函数的基本语法
函数的基本格式要求您首先在函数名前面使用function
关键字来声明函数。函数名可以包含任何字母数字字符和下划线,但不能以数字开头。函数名后面必须跟一组括号和一个用花括号分隔的代码块。在ch3
中用 Komodo Edit 创建一个新的 PHP 文件。称它为test-functions.php
。声明一个命名函数,如下所示:
<?php
function p(){
echo "<p>This paragraph came from a function</p>";
}
如果您加载 http://localhost/CH3/test-functions . PHP,您将看不到任何输出。许多初学者希望看到前面代码的输出。但是函数并不总是像初学者假设的那样运行。在显式调用函数名之前,不会执行函数体内的代码。您可以在test-functions.php
中添加一个函数调用来执行代码,如下所示:
<?php
//function declaration
function p(){
echo "<p>This paragraph came from a function</p>";
}
//function call
p();
再次运行代码,您将在浏览器中看到预期的输出。函数的一个非常有趣的特性是它们可以非常容易地被重用。简单地调用一个函数两次,它就运行两次。开始吧。
<?php
//function declaration
function p(){
echo "<p>This paragraph came from a function</p>";
}
//function calls
p();
p();
你大概能猜对,代码会输出两个<p>
元素,每个元素都有相同的文本:This paragraph came from a function
。更重要的是,您可以看到函数声明和函数调用之间的区别。该示例有两个不同的函数调用。因为函数调用两次,所以会运行两次。
但这是一个极其丑陋的例子。功能非常不灵活。它只能做一件事,即输出那一个字符串。让我们把它变得更聪明一点。
<?php
//function declaration
function p(){
return "<p>This paragraph came from a function</p>";
}
//function calls
$output = p();
$output .= "<h1>Just some heading</h1>";
$output .=p();
echo $output;
现在这样好多了!显著的变化是函数不再有echo
。相反,它返回一个生成的<p>
。这是有后果的。为了让<p>
得到回应,你必须把echo
写在别的地方。在这个例子中,echo
现在出现在最后。
你可能会问为什么这更聪明?那很简单!既然函数没有echo
,如果需要的话,你可以在echo
它之前进一步操作输出。
实际上,这是一个很好的经验法则:不要直接从一个函数开始。使用return
语句要好得多。在代码中的一个地方有一个单独的echo
语句比把echo
语句分散在各处要好得多。
使用函数参数增加灵活性
你可能认为让一个函数总是返回一个内容完全相同的<p>
有点傻。当然,你说得很对。它不是很灵活。所以,让我们用一个函数参数来改进function p()
。
<?php
//function declaration
function p( $content ){
return "<p>$content</p>";
}
//function calls
$output = p( "I want this text in my first paragraph" );
$output .=p( "...and this in my second" );
echo $output;
注意,我在函数声明中的括号内声明了一个名为$content
的变量。这是一个函数参数。$content
用于存储返回的<p>
元素中使用的内容。但是$content
如何获得一个值呢?每次调用函数时都会发生这种情况。函数p()
被调用时使用的参数将被临时存储在$content
中。函数参数非常酷,因为它们允许你编写一个函数,这个函数可以用许多不同的值重用。在本书的后面,你将会看到更多带参数函数的例子。
你考虑过函数名p()
吗?我喜欢我的函数名有意义,因为这个函数总是返回一个<p>
元素,我想p()
会是一个很好的名字。你可以考虑使用另一个名字,比如returnPTag()
。好的函数名应该准确且有意义。无论如何,这是探索 PHP 函数可以做的一些事情的一个小弯路。接下来,让我们使用函数编写一个动态测验。
为测验创建表单
在views
文件夹中创建一个名为quiz-form.php
的新 PHP 文件。
<?php
//complete code for views/quiz-form.php
return "<form method='post' action='index.php?page=quiz'>
<p>Is it hard fun to learn PHP?</p>
<select name='answer'>
<option value='yes'>Yes, it is</option>
<option value='no'>No, not really</option>
</select>
<input type='submit' name='quiz-submitted' value='post' />
</form>";
显示测验表格
要显示测验表格,您必须更新views/quiz.php
中的代码,如下所示:
<?php
$output = include_once "views/quiz-form.php";
return $output;
保存这两个文件,将浏览器指向 http://localhost/ch3/index.php?page = quiz,看看你创造了什么。
使用和
前面的表单使用了两个您可能不熟悉的 HTML 元素。当您希望用户在几个预定义的选项之间进行选择时,<select>
元素是一个很好的元素。选项通过嵌套<option>
元素来显示。其结构非常类似于常规的 HTML 列表,如<ul>
和相应的<li>
。
当用户选择一个选项时,新的 URL 变量将被编码到提交表单时发送的请求中。注意,URL 变量的名称将由<select>
元素的name
属性定义,其值由所选<option>
元素的value
属性定义。
POST 方法
您的第一个表单使用了GET
方法,但它不是唯一可能的 HTTP 方法。还有一种方法叫POST
。POST
方法没有定义最大字符数——事实上,POST
方法甚至不限于文本。当使用 HTTP POST
方法时,可以通过表单上传文件。
此外,HTTP POST
变量在 URL 中不是直接可见的。它们被隐藏起来发送。这使得 HTTP POST
成为必须处理大量内容和带有敏感信息的表单的完美候选。因为 HTTPOST
变量不是 URL 的一个组成部分,所以用户不能将依赖于 HTTP POST
变量的页面视图标记为书签。
使用$_POST 超级全局
PHP 有一个名为$_POST
的原生超级全局。它可以用来访问用POST
方法编码的 URL 变量。提交表单时,您可以使用它来处理表单。更新views/quiz.php
,如下:
<?php
//add a new variable and an if statement
$quizIsSubmitted = isset( $_POST['quiz-submitted'] );
if ( $quizIsSubmitted ){
$answer = $_POST['answer'];
$output = showQuizResponse( $answer );
} else {
$output = include_once "views/quiz-form.php";
}
//keep the return statement as it was
return $output;
//declare a new function
function showQuizResponse( $answer ){
$response = "<p>You clicked $answer</p>";
$response .= "<p>
<a href='index.php?page=quiz'>Try quiz again?</a>
</p>";
return $response;
}
可以加载 http://localhost/ch3/index.php?page =在您的浏览器中进行测验,查看代码的作用。它首先检查表单是否被提交。还记得表单有一个提交按钮吗?
<input type='submit' name='quiz-submitted' value='post' />
好吧,如果 PHP 可以找到用名称为quiz-submitted
的POST
方法编码的 URL 变量,您就知道表单已经提交了。如果表单已提交,您可以使用超级全球$_POST
获得所选答案。然后,答案作为参数传递给新函数showQuizResponse()
,该函数将简单地返回一个字符串来指示用户的答案,并显示一个<a>
元素来重新开始测验。
您制作的第一个表单的action
属性指向 www.bing.com
。提交表单时,测验表单应重新加载测验页面。加载测验的 URL 是index.php?page=quiz
,所以<form>
的action
属性准确地引用了那个资源。
$_POST 是一个数组
你已经知道$_GET
是一个超级全局数组。$_POST
是另一个超全局数组。但是什么是真正的数组呢?基本上,数组是一种可以保存多项的数据类型。每一项都存储在一个索引下。我想分享一个例子。如果您愿意,可以创建一个新的 PHP 文件并编写示例代码,但这并不是真正必要的。我把下面的代码保存在一个叫做test-assoc-array.php
的文件中:
<?php
//complete code for ch3/test-assoc-array.php
$my['name'] = "Thomas";
$my['year-of-birth'] = 1972;
$my['height'] = "193cm";
$out = "My name is " . $my['name'];
echo $out;
如果您在浏览器中运行 http://localhost/CH3/test-assoc-array . PHP,您会看到“我的名字是 Thomas”的输出。在前面的示例中,$my
是一个数组。您可以看到它保存了存储在同一个变量中的数据集合。为了从数组中获取数据,必须使用正确的索引。在前面的示例中,“Thomas”存储在索引[ 'name']
下。在命名索引下存储项目的数组称为关联数组。
检查数组中的所有项目通常会很方便。PHP 有这样一个功能。它叫做print_r()
。下面是使用它的一种方法:
<?php
//complete code for ch3/test-assoc-array.php
$my['name'] = "Thomas";
$my['year-of-birth'] = 1972;
$my['height'] = "193cm";
$out = "<pre>";
$out .=print_r($my, true);
$out .= "</pre>";
echo $out;
如果运行这段代码,可以看到$my
的每个索引及其对应的值。您将看到如下内容:
Array
(
[name] => Thomas
[year-of-birth] => 1972
[height] => 193cm
)
你看到的是 PHP 看到的数组。您会看到一个包含三个命名索引及其值的数组。数组在您的代码中非常有用,因为它们允许您将项目分组在一起。PHP 提供了$_GET
和$_POST
数组,让您可以访问用 http 方法GET
或POST
编码的所有数据。测验示例使用了POST
。我想让您检查一下$_POST
,这样您就可以亲眼看到表单提交后的效果。更新views/quiz.php
中if-else
语句的一些代码,如下所示:
<?php
//complete code for views/quiz.php
$quizIsSubmitted = isset( $_POST['quiz-submitted'] );
if ( $quizIsSubmitted ){
$answer = $_POST['answer'];
$output = showQuizResponse( $answer );
//inspect the $_POST superglobal array
$output .= "<pre>";
$output .= print_r($_POST, true);
$output .= "</pre>";
} else {
$output = include_once "views/quiz-form.php";
}
return $output;
function showQuizResponse( $answer ){
$response = "<p>You clicked $answer</p>";
$response .= "<p>
<a href='index.php?page=quiz'>Try quiz again?</a>
</p>";
return $response;
}
保存工作,将浏览器指向 http://localhost/ch3/index.php?page =测验。如果您提交测验表单,您可以看到$_POST
数组中的所有项目:
Array
(
[answer] => yes
[quiz-submitted] => post
)
您可以从输出中看到,我在提交表单之前选择了yes
,您还可以看到索引quiz-submitted
包含一个值post
。我真正希望您看到的是,当提交表单时,每个带有 name 属性的表单相关 HTML 元素在$_POST
中编码一个命名索引。在views/quiz-form.php
看一看。看看 Submit 按钮是如何对索引quiz-submitted
进行编码的,因为它有一个值为quiz-submitted
的 name 属性。<select>
元素编码answer
索引,因为它有一个值为answer
的 name 属性。PHP 通过$_POST
超全局数组提供对所有编码的测验表单数据的访问。
您将学习使用print_r()
来调试您的 PHP 代码。这个例子只是开胃菜。通常你不想在后台向用户展示传递给 PHP 的数据。您只需检查 P O S T ,看看 P H P 看到了什么。现在您已经看到了,您可以注释掉检查 _POST,看看 PHP 看到了什么。现在您已经看到了,您可以注释掉检查 POST,看看PHP看到了什么。现在您已经看到了,您可以注释掉检查_POST 的代码部分:
//partial code for views/quiz.php
//$output .= "<pre>";
//$output .=print_r($_POST, true);
//$output .= "</pre>";
如果和否则解释
测验依赖于用$_POST
编码的表单数据。它也依赖于if-else
语句。你已经看到过if-else
语句使用过几次。是时候得到关于这种条件语句的更详细的解释了。所有条件语句都遵循一定的模式。
if ( Boolean expression ) {
//code block
}
在任何条件语句的括号内,必须编写一个计算结果为TRUE
或FALSE
、0 或 1 的表达式。在计算机科学中,这样的表达式被称为布尔表达式。如果表达式计算结果为TRUE
,代码块将运行。如果表达式计算结果为FALSE
,代码块将不会运行。
通常,如果表达式是TRUE
就做一件事,如果表达式是FALSE
就做另一件事是很方便的。这可以通过一个else
代码块轻松实现。一般形式如下所示:
if ( boolean expression ) {
code block
} else {
another code block
}
如果表达式是TRUE
,第一个代码块将运行。第二个代码块如果是FALSE
就会运行。您可以在测验示例中看到它的工作情况。PHP 可以检查用户是否提交了表单。如果发生这种情况,PHP 代码将获得用户提供的答案并生成一个响应。如果没有,PHP 将简单地返回显示测验的 HTML。
评估测验回答
现在的测验回答提供的反馈不比鹦鹉提供的多。它只是重复用户选择的答案。有了 PHP,你可以做得更好。在编辑器中打开views/quiz.php
并更新showQuizResponse()
,如下所示:
function showQuizResponse( $answer ){
//changes begin here
$response = "<p>You clicked $answer";
if ( $answer === "yes" ){
$response .= " - I know exactly how you feel!";
}
$response .= "</p>";
//end of changes
$response .= "<p>
<a href='index.php?page=quiz'>Try quiz again?</a>
</p>";
return $response;
}
相同的比较运算符
在前面的代码示例之前,您没有见过任何===
。三个等号组成了 PHP 的相同比较运算符。它比较两个值是否相同。相同的比较运算符通常用于为if
语句制定条件。上一节示例中使用的条件实际上是指“如果用户的回答与‘是’相同。”"
Note
如果你在其他地方找到 PHP 代码示例,你会经常看到==
。双等号表示 PHP 的相等比较运算符。它几乎等同于相同的比较运算符。在大多数情况下,您可以互换使用这两者。在 http://php.net/manual/en/language.operators.comparison.php
了解更多。
卷毛定律:做一件事
你看过 1991 年的电影《都市滑头》吗?是的,那部由比利·克里斯托主演的感觉不错的西部喜剧。杰克·帕兰斯扮演卷毛,一个粗犷的老牛仔,他知道生活的秘密,并不情愿地与克里斯托的角色米奇分享:
- 卷毛:你知道生活的秘密是什么吗?
- (举起一根手指)
- 卷毛:这个!
- 米奇:你的手指?
- 一件事。只有一件事。你坚持这一点,其余的都不算什么。
- 米契:但是“一件事”是什么?
- 卷毛:(微笑)那是你必须去发现的。
我们可以放心,Curly 说的不是干净代码的原则。但是,顺便提一下,他提出了一个原则,我们可以用它来写干净的函数。每个函数都应该做一件事。只有一件事。
Note
Jeff Atwood 写了一篇关于将 Curly 定律应用于干净代码的有趣的博客文章。在 http://blog.codinghorror.com/curlys-law-do-one-thing/
念。
干净的代码是易于使用的代码。如果你的函数只做一件事,它们会很短。短代码通常比长代码更容易阅读和理解。如果你能阅读并理解你的代码,那么发现错误就变得容易多了——而你将会犯错误。如果你花了 50%的开发时间来追踪代码中的错误,不要感到惊讶。
在前面的测试示例中,您可以看到两个 clean 函数,每个函数只做一件事。一个函数显示测验;另一个函数显示一个响应。
有意义的名字
函数和变量名是任意的。你可以给他们打任何电话。在测验中,我们有以下内容:
if ( $quizIsSubmitted ){
$answer = $_POST['answer'];
$output = showQuizResponse( $answer );
} else {
$output = include_once "views/quiz-form.php";
}
我们可以重命名文件、函数和变量,而不会失去任何功能。例如,我们可以:
if ( $a ){
$c = d( $_POST['answer'] );
} else {
$c = include_once "views/e.php";
}
前面的代码很糟糕,因为这些名字根本没有表达意义。代码可以工作,但是很难阅读和理解。阅读这样的代码需要一个非常细心的读者。但是也有可能写出更糟糕的代码。您可以使用容易引起误解的名称。下面的代码仍然是测验示例,它仍然有效,但是变得很难阅读:
if ( $itIsLate ){
$output = goToSleep( $_POST['answer'] );
} else {
$output = include_once "views/coffee.php";
}
代码是诗歌
努力编写有表现力的、漂亮的代码。努力编写易读的代码。当你用代码开发新的解决方案时,你会花大量的时间阅读你自己的代码。代码就像诗歌。你写一遍,却读了很多遍。所以像写诗一样写你的代码:小心选择你的用词。
函数名和变量名应该是描述性的,准确的,不要过长。它们应该让你的代码更容易阅读和理解——而不是更难。通常,你会发现一个函数或变量无法用一个词来准确描述。我经常使用复合变量或函数名,比如$quizIsSubmitted
。我喜欢用骆驼大小写来写这样的名字:每个新单词都大写。Camel case 是一个相当常见的命名约定。我喜欢它,因为我发现 ?? 比 ?? 更容易阅读。
样式表单
第一次尝试设计表单样式时,可能会对不熟悉的元素名称感到困惑。但是您可以像处理任何其他 HTML 元素一样处理表单和大多数相关元素。通常可以完全避免使用id
和class
属性作为 CSS 钩子。使用 CSS 属性选择器,您的表单及其各种属性将为您提供大量的机会来选择您想要的元素。这里有一个让你开始的例子:
/*this selector will target the quiz form only*/
form[action='index.php?page=quiz']{
position:relative;
margin: 30px 10px;
}
/*select only <p> and <select> inside the quiz form*/
form[action='index.php?page=quiz'] p,
form[action='index.php?page=quiz'] select{
display:inline-block;
}
练习
锻炼你所学的是真正学习的好方法。下面是一些简单的练习,可以帮助你理解你所遇到的一些 PHP。其中一些练习可能看起来很简单。您已经编写了代码来解决更复杂的任务。但是从一本书上复制代码示例是一回事。从头开始编写自己的代码是完全不同的事情。
借此机会挑战自己。您可能会发现,通过编写自己的代码来解决简单的问题,您会学到很多东西——也许至少与通过本书中的示例学习一样多。
首先,您可以尝试创建一个外部样式表并将index.php
页面链接到该样式表。如果你忘了怎么做,参考第二章中的提示。
你也可以试着改进一下动态测验。改变views/quiz.php
并让它在用户选择 no 选项时输出有意义的响应怎么样?
您还可以编写另一个 HTML 表单,根据一个人的身高和体重来计算这个人的身体质量指数(身体质量指数)。计算身体质量指数的公式如下。您的任务是创建一个表单,用户可以在上面输入身高和体重,并编写一些 PHP 代码来计算基于输入的身体质量指数。
//metric
bmi = kg/ (2 * m)
//for UK and US readers
bmi = ( lb/(2 * in) ) * 703
最后但同样重要的是,您可以尝试编写一个将货币从一种货币转换为另一种货币的表单。如果你想让它变得更高级,你可以有一个<select>
元素,里面有一个可以转换的货币列表。
摘要
我们在这一章中涉及了很多内容。你已经学会了如何编写 HTML 表单。HTML 表单在提交时会对 URL 变量进行编码。URL 变量通过 HTTP 请求从浏览器传递到 web 服务器。您已经学习了如何使用GET
或POST
方法处理编码了 URL 变量的 HTTP 请求。您已经学会了用命名函数来组织代码。但最重要的是,您已经了解了 Curly 法则以及如何应用它来增强代码的美感。
四、使用图像上传构建动态图库
你知道如何制作一个简单的动态网站。你知道怎么写表格。你知道如何用$_GET
或$_POST
访问 URL 变量。我说是时候好好利用你的新知识了。让我们用一个允许用户上传新图片的表单来构建一个动态图片库。在这个过程中,你会学到不少东西。
- 建立一个动态网站。
- 编写命名函数。
- 使用
$_GET
和$_POST
超全局数组。 - 用一个
while
循环迭代。 - 使用 PHP 的原生
DirectoryIterator
类。 - 编写自定义对象方法。
- 用 PHP 的
$_FILES
超全局数组上传文件。 - 规划并编写一个易于上传文件的类。
建立动态网站
在XAMPP/htdocs
中为这一章创建一个名为ch4
的新项目文件夹。从第三章中复制templates
和classes
文件夹以及里面的 PHP 文件。创建新文件夹css
、views
和img
。
先决条件:一个有一些图像的文件夹
图片库应该有一些图片。此图像库将仅使用 JPEG 图像。为你的图库准备少量 JPEG 图片。将图像保存在img
文件夹中。
我们坚持使用与前几章相同的站点架构。这将使得重用以前项目中的代码变得更加容易。重用您自己的代码将帮助您更快地开发您的解决方案,这最终将使您成为更有价值的开发人员。
创建导航
该网站将有两个主要页面视图:一个用于显示图库,另一个用于显示允许用户上传新图像的表单。因为我们知道我们需要这两个页面视图,所以我们可以准备一个包含两个导航项的站点导航。在views
文件夹中创建一个新文件,并将其命名为navigation.php
。
<?php
return "
<nav>
<a href='index.php?page=gallery'>Gallery</a>
<a href='index.php?page=upload'>Upload new image</a>
</nav>
";
创建两个虚拟页面视图文件
在编写新代码时,从小处着手总是一个好主意。让我们准备两个单独的页面视图:一个用于图库,一个用于上传表单。每个页面视图将从单独的文件中生成和返回。因此,我们在views
文件夹中创建两个文件。
<?php
//complete source code for views/gallery.php
return "<h1>Image Gallery</h1>";
<?php
//complete source code for views/upload.php
return "<h1>Upload new images</h1>";
您会注意到 PHP 块没有结束分隔符,也就是说,代码中没有?>
。你可能还记得第一章中的内容,没有必要结束 PHP 代码块,除非你特别想在你的文件中写一些静态 HTML。只要你只写 PHP,你就不必结束你的 PHP 代码块。另一方面,如果您愿意,您可以用?>
结束 PHP 代码块。无论如何都没什么区别。
创建索引文件
每个网站都应该有一个索引文件。这将是用户唯一会请求的文件,因此,它就像是所有网站内容的大门。让我们创建一个index.php
文件,并显示一个链接到两个非常简单的页面视图的功能性动态导航。
<?php
//complete code for index.php
error_reporting( E_ALL );
ini_set( "display_errors", 1 );
include_once "classes/Page_Data.class.php";
$pageData = new Page_Data();
$pageData->title = "Dynamic image gallery";
$pageData->content = include_once "views/navigation.php";
$userClicked = isset($_GET['page']);
if ( $userClicked ) {
$fileToLoad = $_GET['page'];
} else {
$fileToLoad = "gallery";
}
$pageData->content .=include_once "views/$fileToLoad.php";
$page = include_once "templates/page.php";
echo $page;
测试时间到了
到目前为止,所有的代码都和我们在前几章所做的一样,所以它应该不会给你带来什么惊喜。总的来说,我们只有不到 20 行代码,但是这足以执行一个初始测试。当你在做代码的时候,建议你写一点代码,测试一下,然后再多写一点。
如果你经常测试你的进度,你就能在错误发生的初期就发现它们。在更少的代码行中更容易发现错误,所以让我们尽可能早地捕捉错误。
初学者可能很难知道什么时候测试以及会遇到什么情况。显然,不太了解 PHP 的初学者将很难预测一段 PHP 代码的行为。学会预测 PHP 代码的行为是很重要的。
学习这项技能的最好方法是发挥你的想象力:在你将http://localhost/ch4/
载入浏览器之前,试着猜测你会在那里看到什么。试着猜测你的网站在这一点上会有什么表现。
要执行测试,您必须打开您的 XAMPP 管理器并启动 Apache。Apache 运行后,您就可以将http://localhost/ch4/
加载到浏览器中了。一切如你所料吗?我希望你能看到一个非常基本的网站,动态导航显示两个链接的工作。如果你没有,你应该慢慢地阅读你的代码,寻找任何不合适的地方,例如,拼写错误的变量名。您还可以将您的代码与您以前使用动态导航的项目进行比较。
向一个页面添加两个样式表
在开发更大的网站时,使用多个样式表是很常见的。我们可以用如下代码从index.php
开始做:
$pageData->css = "<link href='css/layout.css' rel='stylesheet' />";
$pageData->css .= "<link href='css/navi.css' rel='stylesheet' />";
您可能还记得,$pageData
对象是在包含templates/page.php
之前在index.php
中创建的。$pageData->css
房产在templates/page.php
使用。本质上,templates/page.php
就像 HTML5 页面的骨架。通过提供在 HTML5 页面中使用的内容,$pageData
对象提供了骨骼的所有肌肉。骨骼和肌肉在index.php
处连接。
上面的代码会工作得很好。但是我想借此机会向您介绍另一个干净代码原则:保持干燥。
保持干燥
所有优秀的程序员都试图保持干燥。DRY 是“不要重复自己”的缩写。当你发现自己在写重复的代码时,你应该经常停下来想一想:有没有一种方法可以重新组织这段代码以避免重复?前面的例子重复了生成<link>
元素的代码。我在这里强调了重复的代码。
$pageData->css =``"<link href='``css/layout.css
$pageData->css .=``"<link href='``css/navi.css
重复是丑陋的!有更聪明的方法来解决这个代码问题。恰好有一种更聪明的方法涉及到一个非常强大的概念:对象方法。让我们实现一个添加样式表的方法。
用方法改进 Page_Data 类
对象方法就像函数一样。最大的区别是方法是在类定义中声明的,通常用于对象属性。这里有一个方法可以让Page_Data
类在添加样式表时避免重复:
<?php
//complete code listing for classes/Page_Data.class.php
class Page_Data {
public $title = "";
public $content = "";
public $css = "";
public $embeddedStyle = "";
//declare a new method
public function addCSS( $href ){
$this->css .= "<link href='$href' rel='stylesheet' />";
}
}
要添加样式表,您必须生成一个具有唯一的href
属性的<link>
元素。要添加另一个样式表,您必须用另一个唯一的href
属性生成另一个<link>
元素。这两个<link>
元素是相同的,除了href
属性。
对象方法addCSS()
利用了这一点。该方法需要一个表示href
属性的参数。每次调用该方法时,都会创建一个新的<link>
元素。所有创建的<link>
元素将一起存储在对象的$css
属性的一个字符串中。
是函数还是方法?
您可以看到方法是用关键字function
声明的。事实上,前面的方法看起来就像您已经编写的一个命名函数。功能和方法几乎相同。唯一的语法区别是方法是在类定义中声明的函数。除此之外,没有明显的区别。
这是什么?
当我提到我的个人财产时,我会用“我的”——比如我的头发、身高和体重。my 这个词是一个自我指称。当从类定义内部引用 PHP 对象属性时,必须使用关键字$this
:这是 PHP 对象用于自我引用的单词。所以,$this->css
是对象对其$css
属性的内部引用。
在前面的方法中,可以看到使用$this
关键字的必要性。没有它,您就不能引用$css
属性。如果你不能引用$css
属性,对象就不会记得创建的<link>
元素。
使用新方法
使用index.php
中的新方法将非常简单。在一个新的Page_Data
对象被创建之后和最终的echo
之前,你可以添加两行代码来添加两个样式表到index.php
页面。
$pageData->addCSS('css/layout.css');
$pageData->addCSS('css/navigation.css');
您需要两个单独的样式表来测试此时是否一切正常。让我们创建两个非常简单的样式表来测试一些东西。
/* code listing for css/layout.css */
h1{color:red;}
/* code listing for css/navigation.css*/
nav a{text-decoration: none; color:black}
nav a:hover{text-decoration: underline;}
现在保存您的文件并在浏览器中加载http://localhost/ch4/index.php
。如果一切正常,所有的<h1>
元素应该是红色的,导航项目应该是黑色的,并且没有下划线,直到你用鼠标悬停它们。这个设计肯定不好看,但是它展示了新的 object 方法的预期效果。
您只能使用声明的方法
PHP 只能按预期使用。您只能使用在类定义中实际声明的方法。在代码示例中,您声明了一个用于添加样式表的方法,因此现在可以添加样式表了。您不能向您的$pageData
对象添加铅笔。
//this would trigger an error
$pageData->addPencil( "2b" );
这似乎是一个非常明显的观察,但是重要的是要认识到,对象只有当你在类定义中声明方法时才具有你给它们的行为。如果您试图在一个对象上调用一个方法,而这个对象的类定义中没有声明这个方法,PHP 将显示一个错误消息。
准备显示图像的功能
在编辑器中打开views/gallery.php
文件,并声明一个简短的函数,该函数简单地返回一个 HTML 字符串,该字符串包含一个带有一个<li>
的<ul>
。
<?php
//complete source code for views/gallery.php
//function call
return showImages();
//function defintion
function showImages(){
$out = "<h1>Image Gallery</h1>";
$out .= "<ul id='images'>";
$out .= "<li>I will soon list all images";
$out .= "</ul>";
return $out;
}
看看函数showImages()
中的变量$out
如何使用增量连接,在几行代码中逐渐获得越来越多的内容。最后,当 HTML 字符串完成时,变量$out
被返回给调用者。调用者将是你的代码中调用函数showImages()
的地方,也就是在views/gallery.php
的开头。
在views/gallery.php
的开头可以看到一个return
语句。一旦 HTML 字符串从showImages()
返回,整个事情就返回到index.php
,因为views/gallery.php
是从index.php
开始包含的。本质上,生成的字符串被返回到index.php
,在那里它将被添加到$pageData
对象,与页面模板合并,并回显到浏览器。
循环
我希望函数、变量和增量连接慢慢开始对您有意义。是时候关注迭代了:在代码中重复一些东西。让我们从while
循环开始。while
只要条件为真,循环将重复相同的代码块。基本语法是
while ( $condition ) {
//repeat stuff here
}
一个while
循环在语法上非常类似于一个if
语句。如果条件成立true
,后续代码块中的代码将重复;它会循环。这里有一个简单的例子来说明这个概念:
<?php
$number = 1;
while ( $number < 5 ) {
echo "the while loop has concluded $number loops<br />";
$number = $number + 1;
}
如果您要运行这段代码,您将会在浏览器中看到如下四行内容:
the while loop has concluded 1 loops
the while loop has concluded 2 loops
the while loop has concluded 3 loops
the while loop has concluded 4 loops
请注意,代码块重复了四次。这是因为括号内声明的条件。它表示只要$number
小于 5,代码块就将重复。代码块每运行一次,$number
就加 1。当while
循环重复四次时,变量$number
的值为 5。因为 5 不小于 5,所以while
循环终止,第五行永远不会被回显。
循环非常适合多次重复相同的操作。PHP 中还有其他种类的循环结构。如果您从其他书籍或互联网上查找 PHP 代码,您可能会遇到它们。其他类型的循环都非常类似于while
循环,尽管语法不同。
使用目录运算符在文件夹中查找文件
我们可以使用一个while
循环为在img
文件夹中找到的每个JPEG
文件创建<img>
元素。但是while
循环不能自己查看文件夹。我们可以使用一个专门为查找文件夹而设计的本地 PHP 对象:它被称为DirectoryIterator
。
迭代只是“循环”的一个技术术语,就像目录是“文件夹”的一个技术名称一样所以,你可以从它的名字猜测出一个DirectoryIterator
可以遍历文件夹中的文件。这就是它所做的一切,而且做得非常好。这里有一个一般的例子:
$filesInFolder = new DirectoryIterator( $folder );
$numItemsInFolder = 0;
while ( $filesInFolder->valid() ) {
$numItemsInFolder = $numItemsInFolder + 1;
$filesInFolder->next();
}
echo "found $numItemsInFolder items in folder named $folder";
看到while
循环的条件了吗?你正在调用DirectoryIterator
对象的方法valid()
。如果DirectoryIterator
对象当前指向文件夹中的一个有效项目,它将返回 true。你大概能猜到方法next()
是做什么的。这将使DirectoryIterator
指向文件夹中的下一项。
因此,通过将一个while
循环与$filesInFolder->valid()
和$filesInFolder->next()
相结合,您可以构建一个循环,重复一个代码块,重复的次数与文件夹中的项目一样多。前面的代码将做到这一点。
显示所有图像
让我们在图库中实现一个类似的代码块。下面是我的最终版本:
//edit existing function
function showImages(){
$out = "<h1>Image Gallery</h1>";
$out .= "<ul id='images'>";
$folder = "img";
$filesInFolder = new DirectoryIterator( $folder);
while ( $filesInFolder->valid() ) {
$file = $filesInFolder->current();
$filename = $file->getFilename();
$src = "$folder/$filename";
$fileInfo = new Finfo( FILEINFO_MIME_TYPE );
$mimeType = $fileInfo->file( $src );
if ( $mimeType === 'image/jpeg' ) {
$out .= "<li><img src='$src' /></li>";
}
$filesInFolder->next();
}
$out .= "</ul>";
return $out;
}
如果保存并运行这段代码,您将会看到 PHP 生成了一个包含<img>
元素的列表,显示了文件夹中所有的 JPEG 图像。如果您的文件夹中有两张 JPEG 图像,您将在您的在线画廊中看到这两张图像,如果您有十张 JPEG 图像,您将看到十张图像。这一切都是因为函数showImages()
而动态发生的。
Note
used Finfo
对象在 PHP 5.3.0 中是默认启用的,但在某些 PHP 安装中可能被禁用。或者,您可以使用mime_content_type( $src )
来获取文件的 mime 类型,但是您应该知道mime_content_type()
已经被弃用了:您不能相信它将来会工作。您可以在该书的配套网站上使用mime_content_type
()找到源代码。
使用一点 PHP,您可以创建对您的客户更有吸引力的解决方案。想一想保持这个图片库的更新有多容易?您的客户只需在正确的文件夹中多放几张图片,图库就会更新。
创建表单视图
你可以写一些 CSS 让画廊更漂亮。我们将会谈到这一点,但首先,我想向您展示如何通过 HTML 表单向图库上传新图像。让我们从显示一个表单开始。从某种意义上说,表单就像站点导航:它是静态 HTML 的一部分,不需要修改。在views
文件夹中为其创建一个单独的文件。调用文件upload-form.php
,如下:
<?php
return "
Upload new jpg images</h1>
<form method='post' action='index.php?page=upload' enctype='multipart/form-data' >
<label>Find a jpg image to upload</label>
<input type='file' name='image-data' accept='image/jpeg'/>
<input type='submit' value='upload' name='new-image' />
</form>";
前面的一些应该看起来很熟悉。我们有一个带有method
和action
属性的 HTML 表单。但是这个表单和你之前写的表单有点不一样。
您注意到为表单声明的enctype
属性了吗?表单使用的默认编码不允许文件上载。我们必须明确声明这个特殊的表单应该使用multipart/form-data
作为content-type
,因为这是通过 HTTP 上传文件所必需的。
另一个显著的不同是新的输入属性type='file'
。它将创建一个文件上传控件,允许用户浏览他们自己的图像文件上传硬盘。还请注意同一个<input>
元素上的accept
属性。它实际上声明了唯一可以通过这个表单上传的文件是带有image/jpeg
的content-type
的文件。
声明一个accept
属性对最终用户非常有帮助。当它被声明时,它将缩小用户可以通过表单选择的文件的范围。帮助用户选择具有适当文件类型的文件。你应该知道accept
属性是不被老版本的浏览器支持的。所以,使用旧浏览器的用户不会得到accept
属性带来的额外好处。但这不会损害表单的基本功能:无论使用哪种浏览器,所有用户都可以选择上传一个文件。
Note
accept
属性可以用于任何互联网媒体类型。互联网媒体类型是识别文件类型的标准方式。在 http://en.wikipedia.org/wiki/Internet_media_type
查看更多互联网媒体类型。
显示用于上传图像的表单
要实际显示上传表单,您必须在适当的时候包含 HTML 片段。您希望当用户单击“上传新图像”导航项目时显示表单。因此,要显示表单,您必须更新views/upload.php
中的代码,如下所示:
<?php
//complete source code for views/upload.php
$output = include_once "views/upload-form.php";
return $output;
如果你保存你的工作并在你的浏览器中加载http://localhost/ch/index.php?page=upload
,你可以看到一个文件上传控件的样子,但是不要期望能够真正上传文件。
Note
文件上传控件在不同的浏览器上会有不同的呈现方式。通常,您会使用一些自定义 CSS 来设计 HTML 元素的外观,但是文件上传控件很难进行样式化。如果你愿意,可以在互联网上搜索解决方案,并准备在多种浏览器和浏览器版本中严格测试你的设计。
$ _ 文件
当您试图通过 HTML 表单上传文件时,可以通过名为$_FILES
的 PHP 超全局数组访问文件日期。在实际上传文件之前,让我们看看 PHP 看到了什么。您可以使用print_r()
来检查$_FILES
,就像您在上一章中使用它来检查$_POST
一样。更新views/upload.php
,如下:
<?php
//complete source code for views/upload.php
//$newImageSubmitted is TRUE if form was submitted, otherwise FALSE
$newImageSubmitted = isset( $_POST['new-image'] );
if ( $newImageSubmitted ) {
//this code runs if form was submitted
$output = upload();
} else {
//this runs if form was NOT submitted
$output = include_once "views/upload-form.php";
}
return $output;
//declare new function
function upload(){
$out = "<pre>";
$out .=print_r($_FILES, true);
$out .= "</pre>";
return $out;
}
在views/upload.php
中声明一个新函数,并在文件顶部添加一个条件语句。HTML <pre>
元素将保留文本格式,比如制表符。原生 PHP 函数print_r()
会输出一个数组,这样人眼就能读懂。
这足以让您测试您的上传表单。保存您的工作并在浏览器中运行。通过表单选择某个.jpg
文件,您应该会看到如下输出。
Array (
[image-data] => Array (
[name] => alberte-lea.jpg
[type] => image/jpeg
[tmp_name] => /Applications/XAMPP/xamppfiles/temp/phpYPcBjK
[error] => 0
[size] => 119090
)
)
从那个输出中,你可以扣除相当多。你可以看到$_FILES
是一个array
。在前面的示例中,$_FILES
有一个索引:image-data
。需要注意的是,它之所以被称为image-data
,是因为表单中的文件上传控件元素的name
属性被设置为image-data
。
在$_FILES['image-data']
里面,还有另外一个数组,有五个索引:name
、type
、tmp
_ name
、error
、size. name
、type
、size
索引应该是显而易见的,剩下的可能第一次碰到就有点晦涩难懂了。
tmp_name
上传表单时,其文件数据将临时存储在 web 服务器的内存中。PHP 可以通过$_FILES
['image-data']['tmp_name']
访问临时存储的文件数据。您必须访问文件数据才能将临时文件永久保存在服务器的文件系统上。
错误
上传文件时可能会出错。该图片库运行在安装了 XAMPP 的本地 web 服务器上。您可能遇到的最常见的问题可能是过于严格的文件权限设置。如果您遇到上传错误,您可以检查$_FILES
[ ['image-data']['error']
以获得相关的错误代码。可以在 www.php.net
查阅 PHP 手册。它可以帮助您理解遇到的任何错误代码的含义。在本书的后面,我将向您展示如何以编程方式处理上传错误。
用 PHP 上传文件
用 PHP 将文件上传到 web 服务器很简单。您只需访问临时文件数据并永久保存它。在此过程中,您必须指明要保存在哪个文件夹中,以及要另存为什么文件名。有一个本地 PHP 函数可以做到这一点。
move_uploaded_file( $fileData, $destination );
该函数有两个参数。第一个是$fileData
,应该保存有效的文件数据。第二个文件夹$destination
应该是一个现有的可写文件夹。函数move_uploaded_file()
返回一个布尔值。如果文件保存成功,它将返回TRUE
,如果出错,将返回FALSE
。
规划上传者课程
作为一名 PHP 开发人员,在您的一生中,您可能需要多次编写代码来上传文件。以这样一种方式编写一些用于上传的代码是一个好主意,这样您可以在以后的项目中轻松地重用它。对象很容易重用,所以计划一个可以重用的类,通过 PHP 对象上传文件。
用户模式
我喜欢使用简单的 UML 类图来规划类。你可以在图 4-1 中看到虚类的基本符号。
图 4-1。
Basic UML diagram
注意类名的命名约定:名字总是以大写字母开头。如果类名是一个复合词,用下划线分隔单词,第二个单词以大写字母开头。
Note
UML 是“统一建模语言”的缩写该语言为记录代码提供了标准语法。UML 不仅仅是这些类图。
上传者类别要求
您知道您将需要保存从表单接收的文件数据。因此,该类需要一个存储文件数据的属性和一个将其保存为文件的方法。您知道文件需要一个名称,所以这个类需要一个$filename
属性。最后,你知道文件必须保存在某个地方。您需要一个属性来记住保存文件的$destination
,并且您可以添加一个方法来指定保存位置。了解了这些需求,您就可以开始计划新的类定义了。你可以绘制一个 UML 类图,如图 4-2 所示。
图 4-2。
UML diagram of the Uploader
class
有了计划和 UML 类图,就很容易开始编写类定义。创建一个新文件classes/Uploader.class.php
,如下所示:
<?php
class Uploader {
private $filename;
private $fileData;
private $destination;
public function saveIn( $folder ) {
$this->destination = $folder;
}
public function save(){
//no code here yet
}
}
前面的代码声明了一个类,该类具有用花括号分隔的类名和类代码块。在该类中,声明了三个属性和两个方法。很容易看出前面的类框架是基于 UML 类图的。
从代码中可以看到,每当调用方法saveIn
时,属性destination
就会获得它的值。属性filename
和fileData
没有任何值。您可以从超级全局数组$_FILES
中获得filename
和fileData
值。如果每当一个新的Uploader
对象被创建时,它们就能得到值,那么它们的值就能反映出你想在那时上传的任何文件,这就太好了。
__
构造魔法()
碰巧的是,您可以声明一个只运行一次的方法,无论何时创建一个新的Uploader
对象。用面向对象的术语来说,这样的方法叫做构造函数。在 PHP 语法中,它被称为__construct
。这是一种所谓的神奇方法。请注意在方法名前有两个下划线字符。为Uploader
声明一个构造方法,这样每当创建一个新的Uploader
对象时,filename
和fileData
属性就可以从$_FILES
中获取它们的值。
<?php
//complete code for classes/Uploader.class.php
class Uploader {
private $filename;
private $fileData;
private $destination;
//declare a constructor method
public function __construct( $key ) {
$this->filename = $_FILES[$key]['name'];
$this->fileData = $_FILES[$key]['tmp_name'];
}
public function saveIn( $folder ) {
$this->destination = $folder;
}
public function save(){
//no code here yet
}
}
还记得您必须知道用于上传文件的<input type='file'>
元素的name
属性吗?您需要name
属性来访问$_FILES
中的所有文件数据。在前面的代码中,构造函数方法将一个$key
作为参数。$key
的值应该与name
属性的值相同。有了这些,构造函数方法就可以访问上传文件的数据,这些数据只驻留在服务器的内存中,直到被保存。
保存上传的文件
课程即将完成。您只需完成保存新文件的方法。当您在本地 web 服务器上工作时,在执行文件上载时可能会遇到一个常见问题:目标文件夹可能不可写。因为您可以预料到该特定错误,所以您可以在代码中为其做好准备,如下所示:
//partial code for classes/Uploader.class.php
//edit the save method in the Uploader class
public function save(){
$folderIsWriteAble = is_writable( $this->destination );
if( $folderIsWriteAble ){
$name = "$this->destination/$this->filename";
$succes = move_uploaded_file( $this->fileData, $name );
} else {
trigger_error("cannot write to $this->destination");
$succes = false;
}
return $succes;
}
通读这段代码,您可能会猜到 PHP 将检查目标文件夹是否可写。如果不是,PHP 将触发一条错误消息,告诉您哪里出错了。换句话说,如果您遇到这个特定的上传错误,PHP 将显示一条错误消息。错误消息是好的;它们帮助您诊断代码中的错误。
使用 Uploader 类
您现在可以充分利用Uploader
类并上传一个文件。在views/upload.php
中不需要很多代码,因为大多数代码都是在Uploader
类中编写的。
//partial code for views/upload.php
//edit existing function in views/upload.php
function upload(){
include_once "classes/Uploader.class.php";
//image-data is the name attribute used in <input type='file' />
$uploader = new Uploader( "image-data" );
$uploader->saveIn("img");
$fileUploaded = $uploader->save();
if ( $fileUploaded ) {
$out = "new file uploaded";
} else {
$out = "something went wrong";
}
return $out;
}
这有多酷?你有一个完全动态的图片库,用户可以通过网站上传自己的图片。也许还不完全是这样,但是我希望你会同意你真的开始使用 PHP 来创造一些有趣和有用的东西。
什么会出错?
我在学习在本地 web 服务器上通过 PHP 上传文件的学生中看到的最常见的错误是,目标文件夹的文件权限设置过于严格。如果您遇到这个问题,Uploader
对象将触发一个 PHP 错误并通知您。解决方案很简单:更改目标文件夹的权限设置,这样每个人都有读/写权限。
另一个常见问题发生在 PHP 通过$_FILES
找不到任何文件数据的时候。如果在创建一个新的Uploader
对象时提供了一个错误的$key
,这种情况就会发生。创建新的Uploader
对象时,必须向Uploader
构造函数传递一个参数。该参数必须包含 HTML 表单中文件上传控件的name
属性。在前面例子中使用的<form>
中,有一个文件上传控件。
<input type='file' name='image-data' />
要上传通过那个<input>
元素接收的文件,您需要一个知道在哪里寻找比特流的Uploader
对象。当Uploader
对象被创建时,您必须将相关的name
属性值作为参数传递。在这种情况下,您必须使用字符串image-data
,如下所示:
$uploader = new Uploader("image-data");
单一责任原则
我希望你惊叹于Uploader
类定义的美丽。它的设计和编写只有一个目的:上传文件。它有三个属性和两个方法。属性是关于要上传的文件的,方法是关于上传文件的。
单一责任原则是面向对象编程中常用的原则。单一责任原则声明一个类应该为单一的目的而编写。该类的所有属性和方法都应该直接与这个目的相关。这个类应该只有一个改变的理由。
例如:Uploader
只有一个改变的理由。如果你想用它上传不同的文件,它会改变。单一责任原则是在代码中为之奋斗的美好理想。这又是真正的卷毛定律,只是这一次,应用于面向对象编程。
Note
你可以在 http://en.wikipedia.org/wiki/Single_responsibility_principle
了解更多关于单一责任原则的内容。
你已经直觉地知道单一责任原则是一个好主意。如果你买了一台多功能厨房实用机器,可以制作咖啡和冰淇淋,烤面包和炸香肠,你会认为它制作的咖啡相当糟糕。事实上,你可以相信它会很糟糕地完成所有的动作。如果你想要很棒的咖啡,你会给自己买一台只有一个目的的机器:咖啡机!
如果你欣赏好咖啡,你可能会想到一些煮不出好咖啡的咖啡机。你正在学习 PHP,这样你就可以计划和构建类来做一件伟大的事情。
摘要
在这一章中,你已经看到了如何使用对象和对象方法创建一个动态图库。您已经看到了一个原生 PHP 对象,并学会了使用它的一些方法,但是您还声明了一个带有属性和方法的自定义类定义。您甚至试图使用while
循环来自动重复代码。
您现在已经编写了两个类定义:Uploader
和Page_Data
。我怀疑你没有完全理解类、对象、属性和方法是什么。在本书中,你将接触到更多的类和对象。在接下来的几页中有大量的例子和解释等着你,所以坚持住。学习需要时间,我们才刚刚开始。。。
现在你已经看够了基本的 PHP。随着您对数据库驱动的动态网站越来越熟悉,我们将很快继续探索数据库,并了解您可以在项目中实现的新的可能性。但是首先,我们将进行一次简短而激烈的迂回,涉及一点 JavaScript 和客户端脚本。