### 《PHP 程序设计》 第三章 PHP 中的数据处理

第三章 PHP 中的数据处理
本章着重讲述 PHP 程序的内部数据，包括数字，变量和常量。程序中的数据由操作符来控制的，操作符告诉 PHP 对内部数据进行什么操作（如加、减等等）。PHP 程序的外部数据包括文本文件和数据库，对外部数据的详细介绍请参考第六章“数据库和 SQL”
当程序运行时数值不会改变，这是因为这些数值在源代码中已经准确表示了它们该是什么值。而大多数时候，在程序运行时却需要改变某些数值，为此，必须留出一部分计算机的存储空间来保存可变化的数值。而且必须随时留意这些小存储单元的位置，以便程序能在需要时能找到它们。像所有计算机语言一样，PHP 使用变量来监视计算机存储器的使用情况，每当需要存储一条新的消息时，就可以将它置成一个变量。常量指的是赋予一个名称的数值，最典型的一个常量例子就是数学值：π（pi）。
本章讲述了在 PHP 中如何使用数值、变量和常量。首先，我们先来讨论数值。
3.1 数值
数值常表示为“等于”或实际代码的形式，比如，在源代码程序中看到像 12.5 这样的数值时，它指的是十二点五，而不是指“1”，“2”，“.”，“5”这四个字符。可以用同样方式来表示文本，比如，“Rolf D"Barno”（注意双引号）表示由十二个字符组成的字符串。因为这十二个字符用双引号括了起来，因此，它们只能是一个字符串数值。
PHP 使用两种类型的数值：
.数字 -- 最基本的数据类型。
.文本 -- 作为一个单元进行处理的一串字符。
3.1.1 数字
在 PHP 中最常使用的是数字，它通常代表为了完成某项任务而需要执行的程序所要用到的一个数值。我们最常使用的是十进制数，但在 PHP 中也可以使用八进制和十六进制。
在遇到很大的或很小的数字的时候，就会发现科学表达式是十分有用的了。在高中时我所学的数学知识差不多都快忘光了，只有对科学表达式牢记不忘，这大概是因为我比较喜欢移动小数点的缘故。科学表达式 10.23E+4,等于 102,300。也可以在科学表达式中用减号表示比较小的数，比如，10.23E-4 等于.001023。简单地说，如果指数为正数的话，就将小数点向右移动，如果指数为负数，则将小数点向左移动。
注意：对于那些对非十进制不熟悉的人来说，这里有一个简单的解释。
十进制的基数为十。当看到值 15 时，它表示（1*10）+5 或 1510。下方的值代表基数。
八进制的基数为八。当看到值 15 时，它表示（1*8）+5 或 1310。
十六进制的基数为十六。当看到值 15 时，它表示（1*16）+5 或 2110。当基数为十六时，除了 0 到 9 外，还需要 6 个字符，以便能占满十六个值的每一个位置。字母 A-F 常用来表示 11-16。因此，值 BD16 等于（B16*16）+D16 或（1110*16）+1310，即 17610。
让我们来看一看在程序中会用到的几种不同类型的数字。首先先来看整数。
* 123--十进制整数。
* 043--八进制整数，数字前缀 0 表示八进制数。
* 0x23--十六进制整数，数字前缀“0x”表示十六进制数。
有小数部分的数叫做浮点数。简略地讲，经常见到的那些数值指的都是浮点数。
* 100.5--有一位小数的一个浮点数，也可以称为一百又十分之五。
* 54.534--有三位小数的一个浮点数，也可以称为五十四又一千分之五百三十四。
* .000034--非常小的一个浮点数，也可以用科学表达式 3.4E-5 表示。
整数没有小数部分，浮点数有小数部分。
3.1.2 文本
文本是一组由引号括起来的字符，因而能被当为单个数据来使用。实际上，PHP 对引号的要求并不严格，没有用引号括起来的单个词也可以用来表示字符串，但是为了避免混淆，请不要这样做。由于文本值包含有一系列字符，所以文本值通常称为字符串。它们在程序中常用于表示确定文件名、显示消息、输入提示等。PHP 严格区分单引号（'）、双引号（"）和反引号（`）的作用。
文本也常称为字符串。
单引号字符串：
单引号字符串相当容易理解，只需要用单引号把想使用的文本括起来即可。例如：
'Men at Arms by Terry Pratchett'
'<p>This is an HTML paragraph.</p>'
单引号字符串是用单括号（'）括起来的文本。
注意：读过本章后面的内容“变量替换”之后，单引号的真正作用才能表现出来。
如果需要在单引号内部再次使用单引号，事情就会变得有些麻烦。例如，下面的语句不能正常工作，这是由于第二个引号已经结束了所表示字符串。
'Terry's book is highly enjoyable.'
这个典型的错误即是语法（或分析）错误，PHP 编译器不知道如何处理第二个单引号以后的文本。以下是正确的表示方式：
'Terry\'s book is highly enjoyable.'
反斜杠（\）字符串表示单引号的功能--结束文本值--将会被忽略。
提示：反斜杠字符也被称为转义字符，这也许是由于它使它后面的字符从常用含义中脱离出来的缘故。
有关单引号字符串的另一个更重要的要点是--如果需要在单引号字符串中换行，只要简单地在源代码中键入换行键即可。清单 3.1 显示了如何这样做。
清单 3.1 line_breaks.php3--在输入行中加入换行即可开始新行
<?php
echo '<pre>First Paragraph:
Corporal Carrot, Ankh-Morpork City Guard
(Night Watch), sat down in his nightshirt
took up his pencil, sucked the end for a
moment, and then wrote:</pre>';
?>
如果读者不熟悉 HTML 语言的话，请阅读一到两本 HTML 教学指南。在本书以后的章节中必须熟悉 HTML 语言。
Page 35, 图 3。1
图 3.1 可以通过 Web 浏览器看到在代码中加入的换行
在图 3.1 中可以看到用单引号括起来的，甚至在代码中包含换行的字符串的一部分。
双引号字符串：
双引号字符串类似于单引号字符串，但是双引号字符串更复杂一点。在双引号字符串中可以使用反斜杠在字符串中加入转义序列和转换字符。也可以使用变量替换，但是现在还不打算过早涉及有关变量替换方面的问题，这个主题留到以后再仔细讨论。
双引号字符串是由双引号（"）括起来的字符串。
基本的双引号字符串是一系列由双引号（"）括起来的字符，如果需要在字符串中使用双引号，可以使用反斜杠字符。例如：
"Men at Arms by Terry Pratchett"
"<p>This is an HTML paragraph.</p>"
"Terry's book is highly enjoyable. "
"John said,\"Gifts are great.\""
注意在最后一行中的反斜杠用来使双引号的功能改变,如果不使用反斜杠字符，也可以使用单引号。双引号字符串和单引号字符串的一个较大的不同之处是，双引号字符串可以在字符串中加入特殊的转义序列。表 3.1 显示了 PHP 可以理解的转义序列。
表 3.1 转义序列
转义序列 描述
\n 换行
\r 回车
\t 制表符
\$ 美圆符号
\0nnn 任一个八进制数
\xnn 任一个十六进制数
\\ 反斜杠字符
提示：在下一节“变量”中，就可以知道在使用$字符时，为什么需要使用反斜杠。
你也许对\0nnn 和\xnn 比较陌生，请看下面的例子：
"Peter was \067 years old yesterday."
"Peter was \0x39 years old yesterday. "
以上的文本都表示彼得 9 岁了，八进制和十六进制序列都表示彼得的岁数 9 这个字符的 ASCII 代码。ASCII 代码的清单详见附录 B。
反引号字符串：
对于反引号字符串是否是真正的文本还存在争论，这是因为 PHP 使用反引号字符串来运行系统命令。当 PHP 看到反引号字符串时，它将字符串数值传递给 Windows、UNIX 或者用户使用的其他类型操作系统。清单 3.2 显示了这个过程是如何完成的，图 3.2 显示在 Web 浏览器窗口中的系统命令输出结果。
反引号字符串使用反引号（`）括起来。
清单 3.2 back_quoted_string.php3--使用反引号字符串执行命令
<?php
echo '<pre>';
echo `ls *.php3`;
echo '</pre>';
?>
Page 37 图 3.2
图 3.2 在 Web 浏览器中显示的系统命令所显示的文本
在双引号字符串使用的转义序列也能在反引号字符串中使用。
3.2 变量
数字仅能解决用来程序当中的内部数据的一部分问题。当需要保存数据值，并在运行程序时需要进行改变的时候，就需要用到变量。PHP 有三种类型的变量：
标量 -- 一次保存一个特定数字或字符串。我通常用 scl_作为标量名的起始字符。如果该变量只保存一种类型的值时，我将在其名字前使用 int_或 str_前缀。
数组 -- 存储数值的列表。这些数值可能是数字、字符串或是另一个数组。我通常用 arr_作为数组变量的起始字符。
对象 -- 存储变量信息和函数。更详细的信息请参看第十章“面向对象”。我通常用 obj_作为对象变量的起始字符。
提示：推荐对不同类型的数据使用可区分的变量名，除非有更好的理由。如果需要使用同一个名字的时候，可以尝试使用名字的复数作为数组变量名。例如，使用 $name 作为标量名，并使用$names 作为数组变量名。这将在以后的编程中避免一些混乱。
注意：在 PHP 中的变量名是区分大小写的。这意味着$scl_varname、$scl_Valname、$scl_varName 和$scl_VARNAME 都代表不同的变量。
在本章下面的部分将分别讨论每一种变量类型。你将读到如何命名变量、设置它的值、以及它们的一些用处。
3.2.1 标量
标量常用来跟踪单个信息。例如，客户的名字或出售的数量。只要标量名以$为第一个字符，第二个字符是字母或下划线，就可以使用任一个可以从名字想象出是什么东西的名字作为标量名。
提示：如果曾经使用 Visual Basic 进行过编程，那么在命名变量时必须特别小心。要记住所有的标量名都是以$为开始的，而不只是字符串；名字的开始字符是$，而不是名字的结尾字符是$。
让我们现在看看一些变量的名字：
* $int_page_number--存储当前页码。
* $str_magazine_title--存储杂志的标题。
* $0--无效的变量名字。变量名不能以数字字符开始。
我较喜欢使用具有描述能力的变量名。对我来说，$int_book_number 比$booknum 更好，这是由于$int_book_number 的描述性更好。由于较长的文件名有助于理解程序，因此在程序不使用比较短的变量名较好。PHP 变量名的长度实际上没有什么限制，但是我一般把长度限制到二十个字符以内。比二十个字符还长的名字，有可能增加产生拼写失误的机会。
使用具有描述性的名字：int_book_number 比 booknum 更好。
明白了什么是标量名之后，现在让我们看看如何给变量赋值。对一个标量赋值通常使用等号，如以下清单 3.3 所示。
清单 3.3 assign_scalars.php3--使用赋值操作符
<?PHP
$int_page_number=46;
$str_magazine_title = 'PHP is good!';
?>
以上代码给变量赋值。当给变量赋给简单的文本值时，由于使用单符号字符串效率更高，所以通常使用单符号字符串。
注意：PHP 使用双斜杠（//）来作为注释的开始，在双斜杠字符后面的任何字符都会被忽略。
对变量赋值以后，可以根据需要改变它们的值。下一个例子，清单 3.4，先对一个变量赋值，然后使用第二次赋值来改变变量的值。第二次赋值使原有的数值加一。
清单 3.4 change_scalars.php3--改变变量的值
<?PHP
$int_page_number = 46;
$int_page_number =$int_page_number +1;
?>
注意：在 PHP 编程中，永远不必声明、定义或分配简单的数据类型（标量或数组），第一次使用变量名就相当于定义它。
3.2.2 数组变量
一个数组就是把一系列数字和字符串作为一个单元来处理。数组中的每一片信息都被认为是数组的一个元素。例如，可以用数组存储一个文件中的所有行或者存储一个地址列表。
只要不用数字作为数组变量名的第一个字符，而且在创建数组名时只使用数字、字母和下划线的时候，就不必操心数组变量的命名规则。
数组元素有三种方法设置初始值，可以对每一个元素分别赋值：
$arr_zoo['pelican'] = 'Bird with a big beak, ';
$arr_zoo['cheetah'] = 'Fast cat. ';
$arr_zoo['horse'] = 'Four-legged animal. ';
也可以用下面的方法同时对多个元素赋值：
$arr_zoo = arry(
'pelican' => 'Bird with a big beak. '
, 'cheetah'=> 'Fast cat. '
, 'horse' => 'Four-legged animal. '
);
最后，最快的方法是简单在数组的下一个空余位置上增加一个元素，第一个位置是 0，第二个位置是 1，依次类推。例如，下面的代码给$arr_names 数组增加了三个元素，这三个元素的下标分别为 1、2 和 3（假设这个数组没有其它元素存在）。
$arr_names[] = 'Mitch';
$arr_names[] = 'Gerry';
$arr_names[] = 'Tim';
在知道了如何给数组元素赋值之后，下一步让我们讨论一下如何如何获取这些值。
为了得到数组名为 arr_zoo，且数组下标为'pelican'的值时，可以使用以下方法：
$key = 'pelican'
$value = $arr_zoo[$key]
这两行代码运行以后，$value 的值变为'bird with a big beak'。文本字符串也可以用来指定要哪一个数组元素的值。例如：
$value = $arr_zoo[pelican];
用于数组下标的字符值不应该用单引号括起来（Perl 称它们为裸词）。由于可以使用裸词，数组下标中不应该有空格。
注意：当读取一个并没有赋值的数组元素时，PHP 返回空或零字符串。
PHP 数组的数组下标和数组值是成对出现的，由于没有一个很方便的方法显示数组中的所有值，这个事实偶尔会引起一些麻烦。在测试和调试程序阶段中，这种功能性的缺乏可能会妨碍程序的开发。虽然本书中还没有提及这些函数，清单 3.5 提供了一个这样的函数。现在只要把 dump_array 函数当成黑箱子即可，等读完本书后，就会明白此函数的含义。把这个例子当成模板，程序中的注释说明了如何使用这个函数。图 3.3 显示了使用 dump_array 函数的结果。
清单 3.5 dump_array.php3--改变变量的值
Page 42, 清单 3.5
注意：dump_array 函数没有按特殊的顺序显示数组下标和数组值对。第四章讨论了可以对数组进行排序的函数。
Page 43，图 3.3，
图 3.3 dump_array 函数显示任一个数组的数组下标和值
以上我们只采用了字符串作为数组下标，让我们思维更活跃一点，考虑一下下面所创建的数组初始值的数组下标是什么？
$arr_mixed = array(
1
,434
, 'Jake' => '23 First Lane'
, 'Rebecca'
);
arry_mixed 数组的数组下标分别是 0、1、Jake 和 2。如果数组下标没有给定，PHP 就自动提供一个。默认的数组下标是以 0 开始的，以后当数组下标没有赋值时默认值每次加一。
可以用标量变量替换所有的数值下标（数字和字符串），并仍能获取它们原有的值。可以这样写：
$key = 1;
echo $arr_mixed[$key];
以上两行将显示 434，让我们用这个例子显示 PHP 是如何按照需要，而把数字数据类型转换成字符数据类型的。在以下的代码行中，数组下标被初始化成字符串：
$key = '1';
echo $arr_mixed[$key];
这两行代码也显示 434，表明了 PHP 可以自动地把字符串转换成数值。
有时，可能需要在数组初始化过程中使用变量。例如：
$int_page_number = 434;
$str_first_name = 'Jake';
$arr_mixed = array(
1
,$int_page_number
,$str_first_name => '23 First Lane'
,'Rebecca'
);
3.2.3 多维数组
对于大多数程序来说，仅有一个简单的数值列表是很不够的。例如，假如既要存书的总页数，又要存储出版商的名字。这需要使用两个列表：lst_number_of_pages 和 lst_publisher_names，在需要增加或修改信息的时候，就很不方便了。并且保证两个列表的同步也留下了隐患。
多维数组提供了一个极灵活的数据结构，每一个数组元素均可以包含另外一个数组。遗憾的是，多维数组（multidimensional）名字实在太长--我更喜欢称它为散列表，它体现了在存贮器中数据结构是如何组织的。
注意：我不打算详细讲述散列表的数据结构是怎样有效的使用存储空间，以及它为什么能快速找到域值。不过，如果你有兴趣的话，我建议阅读一本数据结构方面的书，以便进一步学习。
散列表可以按以下格式进行初始化：
$arr_books = array(
'0-679-76781-9' => array(
'name' => 'The Demolished Man'
,'pages' => 243
,'publisher' => 'Vintage Books'
)
, '0-312-85395-5' => array(
'name' => 'Children of the Mind'
, 'pages' => 349
, 'publisher' => 'Tor Books'
)
);
在上例中使用了每本书的 ISBN 号作为检索数组$arr_books 的散列表下标，且每本书都有它自己的子散列表，用以描述其自己的特定信息。为了查询子散列表中的信息，正常的数组下标被扩展为采用两个下标，例如：要查《The Demolished Man》一书的页数有多少，可以用以下的表达式
$arr_books[0-31-85395-5][pages]
你会发现散列表十分灵活方便。毫不夸张地说，在我写的每一个程序中都会使到散列表。之所以这样，其中一个很重要的原因就是散列表使用起来是如此的灵活，以至于随时都可以加入需要的元素。假如在程序运行过程中，你觉得应该将作者名也存进去的话，那么没有问题，只需简单的在散列表中加入如下信息：
$arr_books[0-312-85395-5][author] = 'Orson Scott Card'。
注意散列表数据结构允许在一个子散列表中加入一项内容，而不会影响其它数据。当开始使用 PHP 从多个数据库表中收集信息时，散列表能用来组合这些信息。例如，当使用一个涉及到多个产品供应商的产品数据库时，可以从第一个产品供应商开始将相关信息存入散列表中。然后，再读取第二个产品供应商的信息，并将这些新信息写入同一个散列表中。将所有产品供应商信息读取、并全部写入散列表以后，这一个散列表就包含了所有的货存信息。
在前面给出的 3.5 列表中的函数 dump_array，阐明了散列表的组织结构。图 3.4 显示了使用 dump_array 函数解释的散列表$arr_books 的组织结构。
Page 46, 图 3.4
图 3.4 使用 dump_arry 函数显示的散列表
3.2.4 变量替换
双引号字符串还有一个特性，由于涉及到变量，我们以前还没有讨论。既然现在我们已经对 PHP 变量是如何工作的已经很熟悉了，让我们再进一步讨论一下双引号字符串。
变量替换 意味着 PHP 可以用在双引号字符串中变量的值替换其名字，把这个概念延伸到通常含义即是变量代表了它们的值。例如，如果$int_count 为 14，那么$int_count + 14 就真正是 14 + 14。PHP，以及大多数计算机语言，这种替换是在字符串内完成的。例如：
$str_size = 'big';
echo "Jack was a $str_size man. ";
将显示
Jack was a big man.
但是，如果需要在变量后面立即跟随非空格或非标点符号外的其它字符时，就会出现问题。以下代码段显示了这是如何发生的：
$str_size = 'big';
echo "Jack was a $str_sizeger man. ";
PHP 不会寻找后面跟有字符串（'ger'）的变量（$str_size），而是寻找变量名为$str_sizeger 的变量。把变量名用大括号括起来可以解决这个问题：
$str_size = 'big';
echo "Jack was a ${str_size}ger man. ";
在使用过变量替换多次之后，字符串和变量的组合就变的很自然。清单 3.6 显示了一些变量替换的例子。
清单 3.6 interpolation.php3--变量替换的例子
Page 47 第 7 行= Page 48 第 13 行 清单 3.6
图 3.5 显示了散列表$arr_books 有三级组成，第三级包含有书籍的名称。正常情况下可以用类似下面的方式访问散列表的命名元素：
echo "The name is
$arry_books[lst_books][0-679-76781-9][name].";
// The previous line of code produces a PHP parse error.
Page 48 图 3.5
图 3.5 使用标量和数组变量进行变量替换
然而不幸的是，当使用变量替换时，PHP3 不允许同时使用多于一个的数组下标。PHP 的安全存取书籍名的方法如下：
$second_level = $arr_books[lst_books];
$third_levwl = $second_level[0-679-76781-9];
echo "The name is $third_level[name].";
3.2.5 动态变量名
PHP 允许用户动态的创建变量名。当程序运行时，使用特殊的符号可以创建新的变量名：
// store the name of the dynamic variable.
$scl_dynamic = 'str_name';
// assign a value to the dynamic variable.
$$scl_dynamic = 'John';
echo "\$str_name = $str_name\n";
此程序将显示
$str_name =John
尽管动态变量名存在一些吸引使用的方面，但是我在二十年的编程经历中，从没有发现有使用它们的需要。数组的灵活性应该足以解决大多数有可能需要使用动态变量名的问题。
3.3 常量
常量可以帮助你以一种简单的方法使程序增加可读性。使用 BUFFER_SIZE 比直接使用 1024 使程序更容易让人理解。此外，由于大多数常量都在程序文件的开头部分定义，对它们进行更改也非常容易。
可以同时定义数字常量和字符串常量。在以下的例子显示了如何这样做：
<?php
define('PI', 3.1415);
define('HOST', '192.168.0.2');
?>
除了不需要在常量名前加$符号外，存取常量值和存取变量值非常类似。在上面定义的两个常量可以用如下的方式存取：
echo 'PI = ' . PI;
echo 'HOST = ' . HOST;
因为不使用初始的$符号，所以变量替换并不适合常量。
3.4 操作符
操作符指挥计算机应该进行什么操作。我们可以这样理解操作符，就好比你在向汽车司机发布“左转”或“右转”的命令。这些命令可以认为是方向操作符，与进行加或减操作的数学操作符有同样的操作方式。然而在另一方面，在汽车行驶时对司机大喊“停车！”的话，这将凌驾于其他命令之上。也就是说，“停车”命令级别高于“左转”和“右转”。
现在，对大部分 PHP 操作符你应该都已经比较熟悉了。在学习操作符时，请相信直觉，已经知道的一些定义也许仍然是对的。
所有操作符都是指挥操作数的。所谓操作数就是可以在其上进行操作的对象。实际上，操作数指的就是数值、变量或者表达式。在本章前面已经学习了什么是数值和变量，表达式指的是由操作符和操作数组成的，进行运算的一个单元。
操作数本身就具有递归的属性。在 PHP 中，表达式 4/2，由两个操作数和一个除号组成，可以认为是一个值为 2 的操作数。再举一个例子：表达式(5+9)-7，由两个操作数和减号组成，第一个操作数是(5+9),第二个操作数为 7。
3.4.1 操作符的优先级
在每一种计算机语言中，操作符的优先级问题都是很重要的，PHP 也不例外。所谓优先级指的是哪一个操作符应该首先计算。PHP 根据相关性决定哪些操作符应放在一起。例如，减号操作符有右到左的相关性，这是因为它立即影响它右边的操作数。你也许没有认识到这一点，甚至用于存取数组元素的方括号也是操作符。表 3.2 包括了所有的操作符，但是不必担忧它们的优先顺序。凭经验，你也许会发现操作符优先级仅仅影响算术操作符和逻辑操作符。
表 3.2 是一系列操作符，并且是按照优先级划分级别--级别越高，它的优先级也越高。在同一级的操作符有同样的优先级，并从左到右进行计算，否则，较高的优先级先计算。使用括号可以精确的控制优先顺序，任何在括号的东西都应该首先进行计算。
表 3.2 优先级的顺序和操作符的相关性
级别
操作符
描述
相关性
15
=>
在数组定义中连接数组下标与值
从左到右
14
->
类操作符
从左到右
13
? :
三重操作符
从左到右
12
<, <=,>, >=
小于，小于或等于，大于，大于或等于
没有
11
==, !=, <>
等于，不等于，不等于
没有
10
+, -, !, ~
正号，负号，逻辑或，位转换
从右到左
09
++, --
加一，减一
从左到右
08
<<, >>
左移位，右移位
从左到右
07
/, *, %
除，乘，取模
从左到右
06
+, -
加，减
从左到右
05
&, .
位逻辑与，字符串连接
从左到右
04
|, ^
位逻辑或，位逻辑异或
从左到右
03
||, &&
逻辑或，逻辑与
从左到右
02
=, +=, -=,
*=，/=, .=,
%=, &=, |=,
^=, <<=, >>=
赋值操作符
从左到右
01
or
and
xor
低优先级或
低优先级与
低优先级异或
从左到右
在讨论单个操作符的例子之前，让我们看看有关操作符优先级的一些特定例子。这样，我们可以验证在表 3.2 中列出的优先级的级别。
下面的例子显示了算术减号操作符比加一操作符有更高的级别：
$a = 5;
$b = $a++;
echo "a = $a<br>";
echo "b = $b<br>";
以上代码行结果将显示
a = 6
b = 5
在变量$a 加一之前，变量$b 被赋予$a 的值。现在让我们看看在负算术加号起作用前，有什么事情发生：
$a = 5;
$b = -$a++;
echo "a = $a<br>";
echo "b = $b<br>";
以上代码行结果将显示
a = 6
b = -5
只有在变量$a 的值进行负操作（换句话说为-6），然后进行加一操作的时候，变量$b 的最后结果值才为-5。这证明了负号操作符比加一操作符的级别更高。1
由于括号内部的操作总是首先执行，所以下面的例子中清楚地显示了除号比加号有更高的级别：
// parens around the first two operands.
echo (5 + 9) / 2;
echo "<br>";
// parens around the second two operands.
echo 5 + (9 / 2);
echo "<br>";
// the default precedence.
echo 5 + 9 / 2;
echo "<br>";
以上代码行结果将显示：
7
9.5
9.5
最后一行所显示的是采用默认优先级的结果，因为结果（9.5）和第二行的结果是一致的，所以可以得出结论，除号操作符是在加号操作符前先执行的。
等号操作符和其它操作符一样也是操作符，而没有什么不同，知道这一点也很重要，等号操作符同样也有优先级。这个概念可能和直觉不一样，下面的例子说明了这个概念。
首先我们用一个简单的赋值语句，把变量$a 设为 1。
$a = 1;
右面的操作数（1），把值赋予了左面的操作数（$a）。现在让我们看一个比较费解的例子，把一个赋予变量的结果赋予另一个变量：
$b = $a = 1;
上面的代码行把数字 1 赋予变量$b，这是因为计算$a = 1 的结果为 1。由于可能会引起混乱，所以我不推荐在程序中使用这种风格，然而也可以偶尔随手使用。现在让我们在以上的代码行中增加 and 操作符。这样，变量$b 是赋予 0，还是 1 的问题也就会被解决。如果 = 操作符的优先级高于 and 操作符，变量$b 将被赋予值$a = 1。如果 and 操作符优先级高于 = 操作符，那么$b 将被赋予值 1 和 0 的与，即 0：
$b =$a =1 and 0;
在以上的代码行执行以后，得出结果是，变量$a 和变量$b 的值都为 1。这样，可以得出 = 操作符有比较高的优先级。
如果想使 and 操作符首先执行（人为地提高它的优先级），可以使用以下的模式：
$b = (($a = 1) and 0);
以上代码行的结果是，$a 被赋予 1，且$b 被赋予 0。
本章的其它部分将着重讨论不同的操作符，并且讨论它们是如何工作。=>操作符在本章较早“数组变量”的部分已经讨论过了。而->操作符也将在第十章“面向对象”中讨论。
3.4.2 三重操作符
三重操作符是在给定的条件下，在两个选择项中做选择。例如：如果公园是在一英里内，那么约翰将会步行去；否则，他会驾车去。语法如下：
CONDITION-PART ? TRUE-PART : FALSE-PART
这是以下语句的缩略形式：
if (CONDITION-PART) {
TREU-PART
} else {
FALSE-PART
}
在第四章“程序控制”中，能找到有关 if 语句的更多信息。
在三重操作符和它的操作数执行后，通常把结果值赋给变量：
$bin_page_two = ($int_page_number == 2) ? 1 : 0;
$bin_page_two 的值依赖于此语句中条件部分 CONDITION-PART($int_page_number==2)运行的结果。如果 CONDITION-PART 结果为真，那么 TRUE-PART 的值将赋给$bln_page_two。如果 CONDITION-PART 结果为假，那么 FALSE-PART 的值将赋给$bln_page_two。
bln_前缀表明此变量应该只保存逻辑值。换句话说，只保存 1 或 0。
在一些参考书中，三重操作符也被称作条件操作符，并且它常用来控制运行代码的哪一个部分。然而，我不推荐使用此操作符，因为它可能使得程序更难读。
在下面的例子中，你将有机会看到这些语句是如何费解。当需要考虑多于两个选择时，可以在三重操作符内部嵌套使用另一个三重操作符。然而当看到下面的例子的时候，就会发现这是些多么令人难懂的代码。
1 ? $int_firstVar++ : $int_secondVar++;
0 ? $int_firstVar++ : $int_secondVar++;
$int_firstVar = $int_temp == 0 ?
$int_number_of_files++ :
($int_temp == 1 ?
$int_number_of_records++ :
($int_number == 3 ?
$int_number_of_bytes++ :
$int_number_of_errors++));
以这种方法编程，会使程序更难理解且难于维护。而使用 if 语句，程序就会变得好看且易于维护代码。更详细信息参见第四章“程序控制”。
3.4.3 算术操作符
算术操作符所反映的东西在学校中已经学过了。加、减、乘和除是绝大多数算术语句中最基本的东西。取模操作符有一点深奥；它是取两个操作数相除的余数。例如，10 % 7 等于 3，这是由于 10 / 7 等于 1，余 3。
当程序需要在一列表中循环，并且每若干项需要运行一些代码时，取模操作符的作用是无法估量的。清单 3.7 显示了当每十项运行一些代码时，应该怎么做。
清单 3.7 modulus.php3--每十项显示一段消息
<?php
for ($index = 1; $index <= 100; $index++) {
if ($index % 10 == 0) {
echo "$index<br>";
}
}
?>
此程序的输出结果应该如图 3.6 所示。
Page 56 图 3.6
图 3.6 使用取模操作符，每十个循环项显示一次
注意是每十项显示一次。当改变取模运算符右边的数值时，将改变在消息显示前有多少项被忽略。改成 15 即意味着每十五项显示一次消息。第四章“程序控制”将详细地描述了 if 和 for 语句。
3.4.4 条件操作符
这些条件操作符将测试一个变量（或数字）和另一个变量之间的关系。例如，5 是否大于 12（5 GREATER THAN 12）？运算条件操作符和它的操作数的结果总是真（值为 1）或假（值为 0）。表 3.3 显示了在 PHP 中的条件操作符。
表 3.3 PHP 中的条件操作符
操作符 描述
判断相等操作符
op1 == op2 如果 op1 等于 op2，返回真。
例如，6==6 为真。
op1 != op2 如果 op1 不等于 op2，返回真。
例如，6!=7 为真。
op1 <> op2 如果 op1 不等于 op2，返回真。
例如，6<>7 为真。
比较操作符
op1 < op2 如果 op1 小于 op2，返回真。
例如，6<7 为真。
op1 <= op2 如果 op1 小于或等于 op2，返回真。
例如，7<=7 为真。
op1 > op2 如果 op1 大于 op2，返回真。
例如，6>5 为真。
op1 >= op2 如果 op1 大于或等于 op2，返回真。
例如，7>=7 为真。
注意：认识到等于操作符是两个等号，而不是一个，这一点非常重要。人们经常忘掉此规则，在测试条件中使用单个等号，这种错误经常在编程过程中发生。
3.4.5 一元操作符
一元操作符只影响单个操作数。它们常用来改变操作数的符号，以及把操作数的值加一或减一。加一即在它原有值的基础上加一，减一即在它原有值的基础上减一。表 3.4 列出了在 PHP 中的一元操作符。
表 3.4 一元算术操作符
操作符 描述
+op1 把操作数的正负号改变为相同
-op1 把操作数的正负号改变为相反
!op1 取操作数的逻辑非
~op1 转换操作数的位值
++op1 在操作数在起作用前，操作数值加一
--op1 在操作数在起作用前，操作数值减一
op1++ 在操作数在起作用后，操作数值加一
op1-- 在操作数在起作用后，操作数值减一
当介绍到一元操作符时，由于增加了难使用的负数，而使操作符变得复杂。没有几个人会编写象这样的数学语句：345 + -23。然而，在 PHP 中你可能使用 354 + $int_gas_bill 的语句，在这里$int_gas_bill 代表$23.00 的债务；换句话说，这是一个负数。
使用正号操作符不会有任何影响，PHP 会忽略它。单个负号操作符可以把一个正数变成负数，或把一个负数变成正数。例如，如果有一个名为$int_first 的变量，它的值为 34。当显示时，-$int_first 将显示-34。
逻辑非（!）操作符常用来把逻辑真转换为逻辑假。例如，!34 为零，且!0 为真。
注意：逻辑真的值为 1，逻辑假的值为 0。
位转换操作符是指把操作数的每一位都从 1 转变为 0，把 0 转变为 1。在我二十年的编程经历中，我从没有觉得必须使用这个操作符。因此，我不再浪费你的时间，提供有关与此的例子了。
如果++或--操作符出现在操作符的前面，那么操作数的值在起作用前，其值就加一或减一。如果++或--操作符出现在操作符后面，那么操作数的值在按需要起作用后，其值再加一或减一。清单 3.8 显示了如何使用先加一的操作符。
清单 3.8 preincrement.php3--使用先加一操作符
<?php
// Original Way
$int_number_of_pages = 5;
$int_number_of_pages = $int_number_of_pages + 1;
echo "$int_number_of_pages<br>";
// New Way
$int_number_of_pages = 5;
echo "++$int_number_of_pages<br>";
?>
此程序运行的结果如下：
6
6
新的编码方式比原有的编码方式要简短些。语句 echo "++$int_number_of_pages<br>",首先$int_number_of_pages 变量加一，然后执行 echo 命令。
先减一操作符的使用方法和先加一操作符的使用方法一样。下一个例子，清单 3.9 显示了如何使用后加一操作符。
清单 3.9 postincrement.php3--使用后加一操作符
<?php
// Original Way
$int_number_of_pages = 5;
$int_current_page_number = $int_number_of_pages;
$int_number_of_pages = $int_number_of_pages + 1;
echo "$int_number_of_pages $int_current_page_number<br>";
// New Way
$int_number_of_pages = 5;
$int_current_page_number = $int_number_of_pages++;
echo "$int_number_of_pages $int_current_page_number<br>";
?>
此程序运行的结果如下：
6 5
6 5
语句$int_current_page_number = $int_number_of_pages++，表示先把变量$int_number_of_page 的值赋给$int_current_of_page，然后变量$int_number_of_page 的值加一。这个例子有助于理解后加一操作符和后减一操作符不会影响在赋值操作符左边变量的值。如果看到了后加一操作符和后减一操作符时，要忽略它们，先执行语句。然后，当执行完以后，然后按需要运用后加一操作符和后减一操作符。
3.4.6 位操作符
位移动操作符，如表 3.5 所示，常用来向左或右按给定次数移动操作数中的所有位。当需要乘或除整数值时，就可以方便地使用比特操作符。例如，数值 3 也可等于二进制的 11，或((1*2)+1)。在二进制中每一个字都表示一个比特位，它是在计算机内存中可以修改的最小单元。
表 3.5 比特操作符
操作符
描述
op1 << op2
左移操作符向左边移动比特位，丢弃最左面的比特位，并且最右面的比特位置 0。每向左移动一位相当于 op1 乘以 2，但效率更高。
op1 >> op2
右移操作符向右边移动比特位，丢弃最右面的比特位，并且最左面的比特位置 0。每向右移动一位相当于 op1 除以 2，但效率更高。
op1 & op2
与操作符比较两个操作数相对应的比特位，如果两个比特位都为 1，那么结果为 1；否则，返回 0。
op1 | op2
或操作符比较两个操作数相对应的比特位，如果两个比特位有一位为 1，那么结果为 1；否则，返回 0。
op1 ^ op2
异或操作符比较两个操作数相对应的比特位，如果两个比特位相同，那么结果为 1；否则，返回 0。
下面的例子中用>>操作符表示除以 4。
$int_a = 128;
$int_b = $int_s >> 2;
echo "$int_b<br>";
此程序运行的结果如下：
32
现在让我们看看在移动操作之前和之后变量的位模式。首先，$int_a 被赋予 128（十进制）或 10000000（二进制）。然后$int_a 的值向左移动两次。所以，移动后的值为 00100000 或 32，然后把 32 赋给变量$int_b。
当向右移动比特位时，最右边的比特位就会丢失。在下面的例子中，用>>操作符代替除以 8 的操作：
$int_a = 129;
$int_b = $int_a >> 3;
echo "$int_b<br>";
此程序运行的结果如下：
16
因为 16 的比特值为 00010000，可以发现最右边的比特位消失了。这里有一个用<<操作符的例子。我们把 128 乘以 8：
$int_a = 128;
$int_b =$int_a << 3;
echo "$int_b<br>";
此程序运行的结果如下：
1024
正如所能看到的，1024 的值是八比特位所能表示的最大值。这说明能使用的比特位并不限于一个字节。事实上，PHP 使用一个标量所表示的字节数量是有限制的，在大多数情况下，这个限制为四。
3.4.7 字符串连接操作符
字符串相加操作符把两个字符串连接起来。如果两个操作数中有一个是数字时，它将自动转化为字符串。下面显示的例子中进行了自动转换：
$str_a = "This box can hold " . 55 . " items.";
echo "$str_a<br>";
这些代码运行的结果如下：
This box can hold 55 items.
数字 55 自动转换成字符串，然后再和其它字符串相结合。注意在最终字符生成时，字符文本中有空格，数字两边也有空格；这样做可以使句子更容易读懂。
也可以用变量当做操作数，进行如下所示的连接操作：
$str_a = 'AAA';
$str_a = 'BBB';
$str_c = $str_a . $str_b;
echo "$str_c<br>";
这些代码运行的结果如下：
AAABBB
注意字符串连接时不需要加入空格或者其它分隔符。如果希望字符串连接以后之间有空格的话，必须保证至少在一个字符串中有空格字符，位置在第一个字符串的尾部，或是在第二个字符串的首部。
3.4.8 逻辑操作符
逻辑操作符执行布尔逻辑操作，或者称为真/假逻辑操作。在这样一个句子中：“如果约翰发烧，而且鼻塞或者耳朵痛，并且年龄不超过 60 岁的话，那么约翰就是感冒了。”该句中的“和”（and），“或”（or）以及“非”（not）就是逻辑操作符。
逻辑操作符常用于程序的 if 或 while 控制语句中。控制语句将在下章“程序控制”中讲述，然而，在这里我们可以先对逻辑操作符做一解释。表 3.6 中给出了 PHP 的逻辑操作符。
表 3.6 逻辑操作符
操作符 说明
op1 && op2 对两个操作数进行逻辑与操作
op1 || op2 对两个操作数进行逻辑或操作
op1 and op2 对两个操作数进行逻辑与操作
op1 or op2 对两个操作数进行逻辑或操作
op1 xor op2 对两个操作数进行逻辑异或操作
逻辑操作符可以控制程序，根据多个不同条件来决定应该执行什么动作。每一个操作符和操作数运算后都会得出一个真或假值。在下面的例子中演示了使用逻辑条件的几种不同方法。
逻辑运算符与（&&）用于确定两个操作数是否都为真。表 3.7 给出了使用与（&&）操作的四种不同组合的结果值。
表 3.7 与（&&）操作结果表
op1 op2 op1&&op2
0 0 0
1 0 0
0 1 0
1 1 1
下面的代码显示了如何使用与（&&）操作符：
if ($int_a == 10 && $int_b == 9) {
echo 'Error!';
};
如果两个操作数至少有一个为假时，都不会执行 echo 命令。顺便说一下，在此例子中显示了==操作符有比&&操作符更高的优先级。每一个==操作符都先执行，且结果值被当成&&操作符的两个操作数。
或操作符（||）用于确定两个操作数是否有一个为真。表 3.8 给出了使用或（||）操作的四种不同组合的结果值。
表 3.8 或（||）操作结果表
Op1 op2 op1||op2
0 0 0
1 0 1
0 1 1
1 1 1
下面的代码显示了如何使用或（||）操作符：
if ($int_a == 10 || $int_b == 9) {
echo 'Error!';
};
如果两个==操作符执行的结果有一个为真时，那么都会执行 echo 命令。
逻辑与和逻辑或操作符都有短路特性。逻辑与操作符的目的是确定两个操作数是否都为真。如果 PHP 确定第一个操作数为假时，那么就不需判断第二个操作数。逻辑或操作符的目的是确定两个操作数是否至少有一个为真。如果第一个操作数为真时，那么就不需判断第二个操作数。
如果不小心的话，那么短路特性可能会成为失误的源泉。例如，在下面的代码段中，如果$int_a++执行结果为真时，那么变量$int_b 将不会加一。
if ($int_a++ || $int_b++) {
echo "true<br>";
echo "a=$int_a b=$int_b<br>";
}
小心：也许你会使用以下代码来确定$int_a 是否等于 9 或 10。但请不要这样做。
if ($int_a == (9 ||10)) {
echo "Error!<br>";
};
PHP 不能正确执行以上代码。测试$int_a 的正确方法是明确地书写每一个需要判定是否为真的子条件。正确的代码方法如下：
if ($int_a == 9 || $int_a == 10) {
echo "Error!<br>";
};
与、或和异或这些优先级比较低的操作符，和它们较高优先级的同伴一样可以执行同样的操作。然而，它们的低优先级特征，使得它们非常适合有条件的执行一些代码，而不必重写 if 语句。不幸的是，低优先级逻辑操作符使用的大多数情况涉及到函数，有关话题本书还没有讨论过。在不考虑有关函数细节的条件下，让我们先看一个或操作符的例子。
每一个 PHP 函数都返回一个值，这个值又常被当成操作数。因而，操作符的短路特性常用于控制程序流。如果或操作符的第一个操作数为真时，那么将执行第二个操作。反过来看，如果第一个操作结果为假时，那么将进行第二个操作。当函数出现错误时，大多数函数返回假。因此，仅仅当出现错误时，或操作符可用来执行代码。例如：
0 or die("Problem connecting to printer.<br>");
echo "Program Over.<br>";
这些代码运行的结果为
Problem connecting to printer.
把或操作符左边的操作数改为 1 时，将显示
Program Over.
3.4.9 赋值操作符
本章以前提到的例子中都使用到了基本的赋值操作符。除了基本的赋值操作符外，PHP 有一些简单的赋值操作符和另一个操作符组合在一起的快捷赋值操作符。例如，代替书写$int_a = $int_a / $int_b，而可以简写为$int_a /= $int_b。使用快捷操作符的优点是，除了可以少输入字符外，使用赋值操作符的目的也会变得非常清晰。
表 3.9 列出了在 PHP 中的所有赋值操作符。在读完本章中其它部分中对不同操作符类型的介绍之后，应该对下表中描述的所有操作符都很熟悉。
表 3.9 赋值操作符
操作符 描述
var = op1 把 op1 的值赋给 var。
var += op1 把 var+op1 的值赋给 var。
var -= op1 把 var-op1 的值赋给 var。
var *= op1 把 var*op1 的值赋给 var。
var /= op1 把 var/op1 的值赋给 var。
var %= op1 把 var%op1 的值赋给 var。
var .= op1 把 var.op1 的值赋给 var。
var &= op1 把 var&op1 的值赋给 var。
var |= op1 把 var|op1 的值赋给 var。
var ^= op1 把 var^op1 的值赋给 var。
var <<= op1 把 var<<op1 的值赋给 var。
var >>= op1 把 var>>op1 的值赋给 var。
你会发现赋值操作符的另一方面不是凭直觉可以想到的，可以象其它操作符一样使用赋值操作符。例如，以下的代码将把变量$int_a 和$int_b 都赋予 3：
$int_a = $int_b = 3;
3.5 总结
本章介绍了有关数值、变量、常量和操作符的概念。数值是直接放在源码中的数，并且程序不能改变它的值。变量放在保存程序数据的计算机内存中。之所以叫变量，是因为可以按需要赋予不同的值。常量从本质上说是命名的数字（例如 π），它对程序归档来说有用，并且使代码更容易安排。操作符是告诉计算机要做什么操作。
数值有时被称为硬编码的数，你应该已经理解了数值，以及它们的三种不同的表示方式，十进制、八进制和十六进制。非常大或非常小的值可以使用科学表示法来描述。
字符串有一点复杂，单引号字符串、双引号字符串和反引号字符串都常用来保存一系列字符。反引号字符串还有一个其它的作用，它们告诉 PHP 把字符串送到操作系统去执行。
转义字符常用来表示难于从键盘输入的字符，或者表示此字符有许多其它作用。例如，如果要在双引号包含的字符串中使用时双引号时，可能会在真正结束输入字符串前，结束此字符串。反斜杠字符被用来改变它原来的含义。
你已经了解了两种类型的变量：标量和数组。标量保存一个单一的数字或文本字符串。数组变量保存有一系列标量。数组中每一个元素都有一个相关联的数组下标，因而，数组是一种保存下标-数值对的方式。当一个数组中包含有另一个数组时，即为多维数组或散列表。在下标和数值对中的数值中是一个数组时，就创建了多维数组或散列表。
变量替换是用来在字符串内部用变量的值来替换变量的。但是，变量替换不能用于多于一级深度的数组。
动态数组名提供了可以随时创建的变量名的能力。由于 PHP 数组允许创建复杂的数据结构，所以我认为不需要这种能力。
当同样的数字在程序中出现多次时，就需要创建常量。使用常量可以把归档的因素加入到程序中。此外，如果在程序的开始部分定义常量时，修改程序就会变得容易些。
你已经学习了操作符是如何命令 PHP 执行什么操作的，一些操作符有比较高的优先级，因而会首先执行它们和它们的操作数。一个操作数可以很简单，如数字 10；也可以非常复杂，包含有变量、数字和其它操作符。这意味着它们本质上是递归的。
PHP 有许多类型的操作符，它们中的大多数都在本章讨论过。刚开始介绍了三重操作符和算术操作符，然后介绍了条件操作符和一元操作符。在本章也介绍了加一和减一操作符，随后，介绍了位操作符和逻辑操作符。当需要快速整数相乘和相除时，有时可能会用到位移操作符。
字符串连接操作符常用来把两个字符串连接起来，赋值操作符是用来给一个变量赋值。大多数赋值操作符都有缩写，这样不仅可以减少输入，而且可以使赋值的意义更加清晰。
下一章“程序控制”将讨论语句的定义，以及如何使用函数来使程序模块化。