3.6 变量
虽然这一章的很多例子中已经使用了变量,但我们还没有正式地介绍变量的概念。这一节就要介绍变量的概念,首先给出变量的定义。简言之,变量(variable)是可以在不同时刻存储不同值的符号。例如,假设你创建一个基于Web的计算器,它能完成数学任务。当然,用户希望输入他选择的值;因此,程序必须能够动态地存储这些值,并完成相应的计算。同时,程序员需要在应用程序中以用户友好的方式来引用保存这些值的地方。这两个任务变量都能完成。
由于这种编程概念相当重要,因此有必要指出声明和处理变量的基本规则。本节将详细地讨论这些规则。
注解 变量是命名的内存位置,其中包含有数据,可以在程序执行期间进行处理。
3.6.1 变量声明
变量总是以美元符$开头,然后是变量名。变量名遵循标识符的命名规则,即变量名可以以字母或下划线开头,要由字母、下划线、数字或从127~255的其他ASCII字符组成。下面是合法的变量:

注意变量是区分大小写的。例如,下列变量之间没有任何关系:

有趣的是,PHP中不需要显式声明变量,这与Perl中不同。相反,变量声明可以与赋值同时进行。但是,可以这样做并不意味着就应当这样做。好的编程实践是:所有变量都应当在使用前进行声明,最好带有注释。
声明变量之后,就可以为其赋值。变量赋值有两种方法:值赋值和引用赋值。下面将介绍这两种方法。
1. 值赋值
按值赋值就是将赋值表达式的值复制到变量。这是最常见的一类赋值。下面是几个例子:

记住,每个变量都拥有表达式赋给它的一个副本。例如,$number和$age都有自己唯一的值12的副本。如果希望两个变量指向一个值的同一个副本,则需要通过引用赋值,下面就将介绍引用赋值。
2. 引用赋值
PHP 4引入了引用赋值的功能,这说明,所创建的变量与另一个变量引用的内容相同。因此,如果多个变量引用了同一个内容,修改其中任意一个变量,在其余的变量上都将有所反映。在等于号后面加一个&符号就可以完成引用赋值。考虑一个例子:

PHP还支持另一种引用赋值语法,即将&符号放在所引用变量的前面。下面是这种语法的一个例子:

引用对于函数参数和返回值也有重要的作用,另外,在面向对象程序设计中同样意义重大。第4章和第6章将分别介绍这些特性。
3.6.2 变量作用域
无论怎样声明变量(按值或按引用),总之在PHP脚本的任何位置都可以声明变量。但是,声明的位置会大大影响访问变量的范围。这个可访问的范围称为作用域(scope)。
PHP变量有4种作用域:
● 局部变量;
● 函数参数;
● 全局变量;
● 静态变量。
1. 局部变量
在函数中声明的变量认为是局部变量,即它只能在该函数中引用。如果在函数外赋值,将被认为是完全不同的另一个变量(即不同于函数中所包含的那个变量)。注意,退出声明变量的函数时,该变量及相应的值就会撤销。
局部变量很有用,因为它消除了出现意外副作用的可能性,否则,这些副作用将导致可全局访问的变量被有意或无意地修改。考虑如下代码:

代码的执行结果为:

可以看到,这里输出了两个不同的$x值。这是因为assignx()函数内的$x是局部变量。修改局部$x的值不会对函数外部的任何值产生影响。同样地,修改函数外部的$x也不会对assignx()内的任何变量有影响。
2. 函数参数
PHP与其他很多编程语言一样,任何接受参数的函数都必须在函数首部中声明这些参数。虽然这些参数接受函数外部的值,但退出函数后就无法再访问这些参数。
注解 本节只适用于按值传递的参数,而不是按引用传递的参数。按引用传递的参数会受到函数内部修改的影响。如果你还不理解这句话,不要担心,因为第4章将详细介绍这些内容。
函数参数在函数名后面的括号内声明。它们的声明方式与一般的变量很相似:

记住,虽然在声明参数的函数内部可以访问和处理这些函数参数,但当函数执行结束时,参数就会撤销。
3. 全局变量
与局部变量相反,全局变量可以在程序的任何地方访问。但是,为了修改一个全局变量,必须在要修改该变量的函数中将其显式地声明为全局变量。这很容易做到,只要在变量前面加上关键字GLOBAL,这样就可以将其识别为全局变量。如果将GLOBAL关键字放在一个已有的变量前面,则是告诉PHP要使用同名的变量。考虑一个例子:

$somevar的值显示为16。但是,如果省略下面这行代码:

变量$somevar将赋值为1,因为$somevar在addit()函数中被认为是一个局部变量。这个局部声明将隐含地设置为0,然后是1,最后显示的值就是1。
声明全局变量的另一种方法是使用PHP的$GLOBALS数组,下一节将正式介绍有关内容。考虑前面的例子,可以使用$GLOBALS数组将变量$somevar声明为全局变量:

返回值如下:
无论选择何种方法将变量转换为全局作用域,都要当心。全局作用域一直以来都是困扰程序员的一个问题,因为草率使用会引发意外的结果。因此,虽然全局变量非常有用,但使用时一定要谨慎。
4. 静态变量
我们讨论的最后一种变量作用域称为静态(static)作用域。函数参数在函数退出时会撤销,与声明为函数参数的变量不同,静态变量在函数退出时不会丢失值,并且再次调用此函数时还能保留这个值。在变量名前面加上关键字STATIC就可以声明一个静态变量:

考虑一个例子:

这个脚本的输出会是什么?如果变量$count没有指明为静态(相应地,$count就是一个局部变量),输出将会如下所示:
但是,因为$count是静态的,它会在每次执行函数时保留前面的值。所以输出如下:
静态作用域对于递归函数很有用。递归函数(recursive function)是一个功能强大的编程概念,它是一个可以重复调用自身的函数,直到满足某个条件为止。递归函数将在第4章详细介绍。
3.6.3 PHP的超级全局变量
PHP提供了很多有用的预定义变量,可以在执行脚本的任何位置访问,用于提供大量与环境有关的信息。可以通过这些变量获得关于当前用户会话、用户操作环境和本地操作环境等详细信息。PHP会创建部分变量,而其他许多变量的可用性和值则取决于操作系统和Web服务器。因此,下面的代码并没有试图收集所有预定义变量及其值的完整列表,而只是输出了与给定Web服务器和脚本执行环境有关的所有预定义变量:

它返回的变量如下所示。在Windows服务器上执行这段代码,花点时间研究一下所生成的变量列表。在后面的例子中还会看到其中一些变量。
可以看到,在此可以得到很多信息,有些信息很有用,有些则不太有用。可以将其中一个变量当作常规的变量进行显示。例如,显示用户的IP地址:

这会返回一个数字型的IP地址,如192.0.34.166。
还可以获得关于用户浏览器和操作系统的信息。考虑以下代码:

这会返回如下的信息:
PHP有9个预定义的变量数组,这个例子只展示了其中的一个。本节剩下的部分将主要介绍每个数组的作用和内容。
注解 要使用预定义变量数组,必须在php.ini文件中启用配置参数track_vars。在PHP 4.03中,track_vars总是启用的。
1.
$_SERVER
$_SERVER超级全局变量包含由Web服务器创建的信息,它提供了服务器和客户配置及当前请求环境的有关信息。根据服务器不同,$_SERVER中的变量值和变量个数会有差别,不过一般都可以找到CGI 1.1规范(可以从美国国家超级计算应用中心(
http://hoohoo.ncsa.uiuc.edu/ cgi/env.html)得到)中定义的变量。你会发现,这些变量在应用程序中都非常有用,其中包括:
● $_SERVER['HTTP_REFERER']:引导用户到达当前位置的页面的URL。
● $_SERVER['REMOTE_ADDR']:客户IP地址。
● $_SERVER['REQUEST_URI']:URL的路径部分。例如,如果URL是http://www. example.com/blog/apache/index.html,那么URI就是/blog/apache/index.html。
● $_SERVER['HTTP_USER_AGENT']:客户的用户代理,一般会提供操作系统和浏览器的有关信息。
2.
$_GET
$_GET超级全局变量包含使用GET方法传递的参数的有关信息。如果请求URL
http://www.example.com/index.html?cat=apache&id=157,就可以使用$_GET超级全局变量访问如下变量:

默认情况下,要访问通过GET方法传递的变量,$_GET超级全局变量是唯一的途径。不能用$cat、$id等方式来引用GET变量。之所以推荐使用这种方式访问GET信息,原因将在第21章解释。
3.
$_POST
$_POST超级全局变量包含用POST方法传递的参数的有关信息。考虑如下用于请求用户信息的表单:

通过目标脚本subscribe.php,就可以使用下面的POST变量:

与$_GET一样,在默认情况下,$_POST超级全局变量是访问POST变量的唯一方式。不能用$email、$pswd、$subscribe等方式引用POST变量。
4.
$_COOKIE
$_COOKIE超级全局变量存储了通过HTTP cookie传递到脚本的信息。这些cookie一般是由以前执行的PHP脚本通过PHP函数setcookie()设置的。例如,假设使用setcookie()存储了一个名为example.com、值为ab2213的cookie。以后就可以通过调用$_COOKIE["example.com"]来获得这个值。第18章将详细介绍PHP的cookie处理功能。
5.
$_FILES
$_FILES超级全局变量包含通过POST方法向服务器上传的数据的有关信息。这个超级全局变量与其他的变量略有不同,它是一个二维数组,包含5个元素。第一个下标表示表单的文件上传元素名;第二个下标是五个预定义下标之一,这些下标描述了上传文件的某个属性:
● $_FILES['upload-name']['name']:从客户端向服务器上传文件的文件名。
● $_FILES['upload-name']['type']:上传文件的MIME类型。这个变量是否赋值取决于浏览器的功能。
● $_FILES['upload-name']['size']:上传文件的大小(以字节为单位)。
● $_FILES['upload-name']['tmp_name']:上传之后,将此文件移到最终位置之前赋予的临时名。
● $_FILES['upload-name']['error']:上传状态码。尽管这个变量名为error,但实际上在成功的情况下也会填写这个变量。它有五个可能的值:
n UPLOAD_ERR_OK:文件成功上传。
n UPLOAD_ERR_INI_SIZE:文件大小超出了upload_max_filesize指令所指定的最大值。
n UPLOAD_ERR_FORM_SIZE:文件大小超出了MAX_FILE_SIZE隐藏表单域参数(可选)指定的最大值。
n UPLOAD_ERR_PARTIAL:文件只上传了一部分。
n UPLOAD_ERR_NO_FILE:上传表单中没有指定文件。
第15章将全面介绍PHP的文件上传功能。
6.
$_ENV
$_ENV超级全局变量提供PHP解析器所在服务器环境的有关信息。此数组中的变量包括:
● $_ENV['HOSTNAME']:服务器主机名。
● $_ENV['SHELL']:系统shell。
7.
$_REQUEST
$_REQUEST超级全局变量是个“全能选手”,它记录了通过各种输入方法传递给脚本的变量,特别是GET、POST和cookie。这些变量的顺序不依赖于它们在发送脚本中出现的顺序,而是依赖于variables_order配置指令所指定的顺序。虽然它很诱人,但不要用这个超级全局变量处理变量,因为它不安全,原因请参考第21章。
8.
$_SESSION
$_SESSION超级全局变量包含与所有会话变量有关的信息。注册会话信息能为你提供便利,这样就能在整个网站中引用这些会话信息,而无需通过GET或POST显式地传递数据。第18章将主要介绍PHP强大的会话处理功能。
9.
$GLOBALS
$GLOBALS超级全局变量数组可以认为是超级全局变量的超集,包含全局作用域内的所有变量。执行下面的代码可以查看$GLOBALS中的所有变量:

3.6.4 变量的变量
有时候,你可能希望使用这样一个变量,它的内容本身可以动态地视为变量。考虑一个典型的变量赋值:

有趣的是,接下来可以在原变量名前面再加一个美元符,这就会将其值spaghetti作为一个变量,可以再为它赋另一个值:

其作用是把& meatballs赋给名为spaghetti的变量。
因此,下面两行代码将得到相同的结果:

两个结果都是字符串spaghetti & meatballs。