PHP 入门十大技巧

作者:Marco Fioretti

毫无疑问,PHP 是最容易入门的语言之一,可以用来生成动态 Web 内容。PHP 与 Linux、Apache 和 MySQL 的结合非常流行,以至于衍生出了 LAMP(Linux、Apache、MySQL 和 PHP)这个词。许多页面上线时,其作者无需自行设置或编程任何内容。他们只需在搜索引擎中找到一些预先编写好的代码片段,将其原样粘贴到 HTML 模板中,将所有内容上传到他们的 Web 服务器,就完成了。

或者他们是这么认为的。即使以前有编程经验也可能帮助不大,因为为桌面或 Web 编程是两种截然不同的范例。因此,通常情况下,当人们剪切和粘贴 PHP 代码时,什么也没有发生(至少没什么好事发生)。页面加载 非常 缓慢,或者更糟的是,程序员选择的 PHP 代码打开了一个新的安全漏洞。

下面的技巧是专门为已经了解编程基础知识,但以前从未接触过 PHP 的用户编写的。它们大致可以分为三类:如何正确开始,如何避免伤害自己,以及最后,如何提高代码效率。由于篇幅限制以及已经有大量关于 PHP 的优秀在线和纸质文档,大多数技巧仅解释需要注意什么以及原因。

1. 检查所有内容是否已正确安装和配置

PHP 初学者常见的一个困惑是将他们的第一个 Web 页面上传到某个服务器,然后在浏览器中只看到 PHP/HTML 源代码,而不是预期的内容。发生这种情况是因为 Web 服务器不认为该文件是应该传递给 PHP 解释器的东西。原因是系统管理员忘记将 PHP 文件与 PHP 解释器关联起来。您可以在 Apache 配置文件或本地 .htaccess 文件中执行此操作。这是一个示例配置行

AddType application/x-httpd-php .php3 .php

事实上,只需将这个非常简短的页面上传到您的 Web 空间,就可以知道情况如何

<HTML>
<HEAD>
<TITLE>PHP Configuration Check</TITLE>
</HEAD>
<BODY><? php phpinfo() ?>
</BODY>
</HTML>

运气好的话,结果将类似于图 1 所示。phpinfo() 函数会打印出 PHP 的编译方式以及所有配置变量的值。此函数为您提供了大量有用的信息。每当您在在线 PHP 论坛上寻求支持时,它的输出很可能是您被要求提供的第一件事。

Top Ten Tips for Getting Started with PHP

图 1. phpinfo() 函数生成的示例 PHP 信息

2. 让 PHP 和脚本告诉您有关错误的信息

为了加快调试速度,您可以告诉 PHP 和 Apache Web 服务器哪些错误必须报告以及何时报告。php.ini 配置文件中的 error_reporting 变量可以看作是一系列(位)标志。它们中的每一个都可以单独设置,以检测(或不检测)特定类别的错误。例如,此指令

error_reporting = E_ALL

将从简单警告到严重错误的所有内容发送到浏览器,但前提是另一个变量 display_errors 已打开。php.ini 文件中的常规 PHP 设置可以在 Web 服务器级别被覆盖。使用 Apache 时,与上述指令等效的指令是(在 httpd.conf 中)

php_flag  display_errors      on
php_value error_reporting	2047

如果您无法访问 PHP/Web 服务器配置(这很常见),则可以通过将此命令添加到您的脚本中来实现相同的结果

error_reporting(E_ALL);

说到 Web 服务器,还要记住检查它们的错误日志,以确切了解哪一行代码导致脚本崩溃。

如果在所有这些技巧都无法找到任何错误后,脚本仍然失败,则几乎可以肯定错误出在脚本逻辑本身。在某个地方,某个变量被分配了一个您认为不可能的值,这会使代码的其余部分感到困惑。当变量实际上是在运行时构建并传递给数据库服务器的 SQL 语句时,也适用这种情况。

解决方案是在浏览器上显示该变量。您可以使用通常用于将 HTML 代码发送到浏览器的 print() 指令轻松地做到这一点。die() 语句的作用与 print() 相同,但它也会立即停止脚本。

3. 标头必须在任何内容之前!

您甚至可以在开始构建实际的 Web 页面之前生成和传输任何类型的 HTTP 标头。但是,您必须记住,header() 必须在任何 HTML 代码或 PHP 输出(包括空白行或空行!)之前调用!例如,像这样的代码

<?php /* any PHP command(s) here */ ?>

<?php header("Content-type: image/png"); ?>

将无法工作。两个编码的 PHP 语句之间仅存在空行就会导致 PHP 传输标准 HTTP 标头,这几乎肯定不是您想要的(否则您就不会使用该函数)。请注意,空白行甚至可能...在另一个文件中。也就是说,如果您从某个外部文件加载 PHP 代码,而该文件并非完全以结束的 ?> PHP 标记结尾,则会发生同样的事情。

对于构建使用 cookie 的站点的程序员来说,这是一个经常引起头痛的原因。使 cookie 工作的唯一方法是在您的 PHP 程序发送标头信息之前处理它们。如果您没有意识到一个简单的空白行会发送标头信息,您可能会盯着您的代码几个小时,想知道为什么 cookie 会出现问题。毕竟,您确实在有意发送标头之前处理了 cookie。您不一定注意到的是,您的程序(或包含的文件)中有一个空白行,该行在您不知情的情况下发送标头,这就是为什么您的 cookie 不起作用的原因。

4. 始终检查用户数据(并注意电子邮件地址)

您应该始终验证您的页面从 Web 接收的数据。JavaScript 例程在用户浏览器上验证表单输入在安全方面是无用的。没有什么可以阻止黑客直接向您的代码发送恶意数据。想象一下一个 PHP 购物车,它可以显示用户决定的 $HIGHEST_PRICE 以下的所有商品。如果事先没有检查,您就愉快地执行了一个数据库查询,其中 $HIGHEST_PRICE 的值类似于“delete * from my_database;”,那么当您的在线商店看起来是空的时,请不要抱怨!

您可以使用三种技术的组合来验证数据。第一种是使用正则表达式分析数据,正则表达式显式定义了仅允许的格式;例如,电话号码或出生年份只能包含数字,因此请通过函数 is_digit() 传递它。

第二种是使用其他函数,例如 EscapeShellCmd(),它可以阻止“数据”执行不需要的系统命令,或者 mysql_escape_string() 用于必须插入 SQL 语句的变量。

最后一种验证类型严格取决于变量的实际含义及其使用的上下文。只有您才能在这里帮助自己。例如,5555555 仅由数字组成,但(在北美)它不是有效的电话号码。只有当用户声明来自另一个国家/地区时,才应允许使用它。同样,尽管 18 是完全有效的 $AGE,但向老年人提供折扣的脚本应该拒绝它,对吧?

从这个角度来看,电子邮件地址尤其麻烦。有几个函数可以验证它们的语法正确性,例如 www.zend.com/tips/tips.php?id=224&single=1 上的那个。但是,它们无法保证地址确实属于发送者,或者地址是否根本存在,例如 Luke.Skywalker@whitehouse.gov。好吧,很可能可以安全地假设白宫中没有 Luke.Skywalker。始终要求用户回复确认消息或打开到他们邮件服务器的套接字以检查它们是否存在。

5. 正确管理引号和转义符

如果您加载这个非常简单的 PHP 代码,您的浏览器中会出现什么?

<? php
$HOME = 'a sweet place';
print "1: $HOME<br>"; // double quotes
print '2: $HOME<br>'; // single quotes
?>

答案是这两行文本

1: a sweet place
2: $HOME

双引号使 PHP 将其中包含的任何变量替换为其当前值。单引号的内容被视为一个整体的、不透明的块,只能复制或打印,不能修改。当您使用引号来构建关联数组的键时,也适用相同的情况。$my_array['$HOME'] 和 $my_array[“$HOME”] 将是不同的元素。就是这样。尽管如此,仍然很容易忘记这种区别,并在您想要另一种引号时使用一种引号,或者根本不使用引号。因此,当某些内容的值与您期望的不同时,请首先检查引号。

由于用户数据不可信,因此可以将 PHP 设置为自动使用反斜杠转义所有 HTML 表单发送到脚本的 $_POST。实际上,即使是内部数据也可能包含反斜杠,以转义特殊字符,必须在处理之前将其删除。解决方案是使用 stripslashes 函数,如这个直接来自在线 PHP 手册的示例所示

<?php
$str = "Is your name O\'reilly?";
// Outputs: Is your name O'reilly?
echo stripslashes($str);
?>

6. 让数据库完成工作,而不是您的脚本

如上所述,PHP 与 MySQL 一起使用非常频繁,以至于 LAMP 首字母缩写词是 Web 设计中最著名的组合之一。因此,编写更快 PHP 脚本的最佳方法之一是充分学习 MySQL,使其尽可能多地工作,而不是 PHP。以下两个代码片段说明了这个概念

<?php //find all the books that Asimov wrote after 1980
$sql = "select YEAR, BOOK from MY_BOOKSHELF where AUTHOR LIKE 'Asimov' ; ";
  if ($sql_res = mysql_query("$sql")) {
    while ($r  = mysql_fetch_array($sql_res)) {
      if ($r[YEAR] > 1980) {// print the book title ;}
   }
?>

和,

<?php //find with MySql all the books that Asimov wrote after 1980
$sql = "select BOOK from MY_BOOKSHELF where AUTHOR LIKE 'Asimov'  AND YEAR > 1980;";
  if ($sql_res = mysql_query("$sql")) {
    while ($r = mysql_fetch_array($sql_res)) {
      // just print all the returned titles ;
}
?>

第二个版本将比第一个版本运行快得多,因为数据库引擎旨在尽可能快地选择所有和仅与任何标准组合匹配的数据。在这种类型的任务中,它们总是比 PHP 快得多。因此,请确保尽可能多的选择逻辑都在 SQL 查询内部,而不是在构建和使用它的 PHP 代码中。当然,整个技巧同样适用于您与 PHP 一起使用的任何其他数据库引擎。

7. 编写可移植的文件管理代码

文本文件中的行尾在每个操作系统系列中以不同的方式编码。二进制文件(例如图像或压缩存档)更糟糕,因为即使一个损坏的字符也可能使整个文件无用。实际上,这意味着您有责任编写将在您可能使用的任何平台上以相同方式管理文件内容的代码。即使您确定您和您的 Web 服务器将始终并且仅运行 GNU/Linux,情况仍然如此。否则,您可能会发现您的图像或文本处理代码中没有错误,直到您使用它从朋友的 Windows 或 Apple 计算机上传文件!

就 PHP 而言,解决方案是正确使用 fopen() 系统调用的 t(文本模式转换)和 b(二进制)标志。详细信息请访问 www.php.net/function.fopen。请注意,该页面明确建议:“为了可移植性,强烈建议您重写使用或依赖 t 模式的代码。”

8. 了解字符串处理函数

Web 页面仍然主要由文本组成,许多数据库也是如此。这就是为什么优化文本分析和处理是使您的所有脚本运行更快的最简单方法之一。正则表达式是为这类工作量身定制的,但它们看起来像象形文字,甚至可能并不总是最佳解决方案。PHP 虽然没有达到相同的极端(呃,我们指的是 Perl 的强大功能和灵活性),但有不止一个函数的工作方式与正则表达式完全相同,只是速度快得多。我们指的是 str_replace()、strcmp()、strtolower()、strtoupper()、strtr()、substr()、trim()、ucfirst() 和其他几个函数。花一些时间研究手册中的它们,这将是非常值得的。

9. 将布局和编程分开

使任何网站的源代码不可读且难以更新的肯定方法是将大量的 PHP 和 HTML 代码交织在一起,即使每个 PHP 片段在页面中仅使用一次,如以下示例所示

myfile.php>
<!-- lots of HTML code for static header, logo, menus...>
<?php lots of PHP code generating a list of the latest news ?>
<!-- lots of HTML code for the central part of the page...>
<?php lots of PHP code creating a per-user list of the
   most popular pages ?>
<!-- lots of HTML code for the user feedback form...>

与其犯这个错误,不如将每个 PHP 代码片段封装在一个或多个函数中,然后将它们全部放在一个单独的文件中(没有任何 HTML 代码),该文件将使用 include_once 命令加载。结果将更加简洁且易于维护

myfile.php>
<?php include_once ("common_code.php"); ?>
<!-- lots of HTML code for the static page header, logo,
menus...>
<?php show_latest_news (); /* only one function call */ ?>
<!-- lots of HTML code for the central part of the page...>
<?php show_most_popular_pages (); /* only one function call */ ?>
<!-- lots of HTML code for the user feedback form...>

这种方法的另一个巨大优势是,通过简单地包含上面显示的 common_code.php,您网站的任何页面都将能够使用这些相同的函数。更重要的是,如果修改了任何函数,新版本将立即在所有页面中可用。

10. 检查函数和系统调用的结果

最后但并非最不重要的一点是,所有 PHP 函数都必须向调用它们的代码返回可接受的数据。这个看似多余的陈述的棘手之处在于,可接受的含义取决于整个脚本,并且可能随时不同。这是一个非常愚蠢但有效的例子,说明了我们的意思

function subtraction($A, $B) {
	$diff = $A - $B;
  	return($diff);
}
   $C = 1/subtraction(3, 3);      // ERROR! Division by Zero!
   $D = 1/(1 - subtraction(3,3);

虽然计算 $C 会使脚本崩溃,但计算(使用相同的操作数)$D 不会。关键是在对变量执行任何操作之前,您应该检查它是否具有可接受的值。在上面的示例中,这意味着将减法结果分配给一个辅助变量,并且仅当它为非空时才继续进行除法。

检查系统调用的返回值甚至更重要,即,为允许与外部进程和文件交互而提供的内置函数。如果您忘记检查返回值,则可能会丢弃数据而无人注意,如以下示例所示

<?php
$HANDLE = fopen("newuser.txt","w")); // open a file
fwrite($HANDLE, "New User Data");    // write to it
?>

如果 fopen 失败(例如,因为磁盘已满或者您没有写入权限),则新用户数据将永远丢失。在写入之前,请检查 $HANDLE 是否不为空

<?php
if (!$HANDLE = fopen("newuser.txt","w")) { die "File access failed: newuser.txt"; }
fwrite($HANDLE, "New User Data");
?>

祝您 PHP 编码愉快!

Marco Fioretti 是一位硬件系统工程师,对自由软件作为 EDA 平台以及作为 RULE 项目的当前负责人,作为高效的桌面感兴趣。Marco 与他的家人住在意大利罗马。

加载 Disqus 评论