PHP 变量
变量基础
在 PHP 中,变量命名需要遵循一套既定的规则,这些规则与 PHP 中其他类型的标识符或标签保持一致。一个正确的变量名应当满足以下条件:
- 起始字符:变量名必须以字母(小写 a-z 或大写 A-Z)或者下划线
_
开头。所以,数字不能作为变量名的首字符。 - 后续字符:首字符之后,可以跟随任意数量的字母(a-z, A-Z)、数字(0-9)或下划线
_
组合。变量名可以是单个字符,也可以是多个字符的组合。
用正则表达式来描述这一规则,就是:^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$
。这里简单解释一下正则表达式的含义:
^
表示字符串的开始。[a-zA-Z_\x80-\xff]
定义了首字符可以是任何大小写字母、下划线,或者是 ASCII 字符集中的128到255之间的字符(这涵盖了扩展 ASCII 字符)。[a-zA-Z0-9_\x80-\xff]*
表示首字符之后可以跟任意数量的字母、数字、下划线或上述提到的 ASCII 扩展字符。$
表示字符串的结束。
特别说明:
- 当我们提及“字母”时,不仅包括基本的英文字母,还涵盖了 ASCII 码范围从128到255的字符,这部分字符包含了西欧语言中的特殊字符等。
注意事项:
$this
是一个在面向对象编程中具有特殊意义的变量,它自动引用当前对象实例本身。由于其特殊性,$this
不能被直接赋予新的值。- 在 PHP 7.1.0 之前的版本中,有一种称为“间接变量赋值”的特性,允许通过变量变量(即一个变量的名称存储在另一个变量中)来改变像
$this
这样的特殊变量的值。但从 PHP 7.1.0 开始,这种做法已被禁止,以增强代码的安全性和一致性。
示例代码展示了合法与非法的变量命名:
<?php
$var = 'Bob'; // 合法变量名
$Var = 'Joe'; // 合法,但与$var是两个不同的变量
$_4site = 'not yet'; // 合法,以_和数字组成
$i站点is = 'mansikka'; // 合法,包含中文字符,但不推荐这样做
// 非法变量名,因为以数字开头
// $4site = 'not yet';
?>
传值赋值
PHP 中的变量默认采用传值赋值方式。当把一个变量的值赋给另一个变量时,实际上是创建了一个新的变量副本,两者在内存中是独立的。修改一个变量的值不会影响到另一个变量。
例如:
<?php
$a = 10;
$b = $a; // 这是一个传值赋值
$a = 20;
echo $b; // 输出 10,因为$b是$a原始值的一个副本
?>
引用赋值
与传值赋值相对的是引用赋值,可以通过在等号前添加一个&
符号来实现。引用赋值使两个变量指向同一块内存地址,因此改变其中一个变量会影响到另一个。
下面的代码片段展示了如何使用引用赋值。在这个例子中,变量$foo
最初被赋值为字符串'Bob'
。接着,通过在$foo
前面加上&
符号,我们创建了一个对$foo
的引用,赋给了变量$bar
。因此,当$bar
的值被修改时,由于它实际上是指向$foo
所占用内存的引用,$foo
的值也随之改变。
<?php
$foo = 'Bob'; // 将 'Bob' 分配给 $foo
$bar = &$foo; // 通过引用将 $foo 分配给 $bar
$bar = "My name is $bar"; // 修改 $bar,同时也影响了 $foo
echo $bar; // 输出 "My name is Bob"
echo $foo; // 输出也变为 "My name is Bob",因为 $foo 和 $bar 引用同一个值
?>
引用赋值限制
需要注意的是,引用赋值只能应用于已命名的变量之间。试图对一个表达式或函数返回值直接使用引用赋值是不允许的,因为这些没有具体的变量名,无法作为持久的引用目标。
<span style="color: rgba(0, 0, 0, 0.85); font-family: -apple-system, "system-ui", "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: 14px; text-wrap: nowrap;">$bar = &(24 * 7); // 错误:不能引用一个临时表达式的值
$bar = &test(); // 错误:即使 test() 返回一个值,也不能直接对其结果应用引用赋值</span>
初始化变量
虽然 PHP 允许使用未经初始化的变量,但最好还是在使用前明确地初始化它们。这不仅有助于提高代码的可读性和可维护性,还能减少潜在的错误。未初始化的变量会根据其类型自动赋予一个默认值:
- 布尔型:
false
- 整型和浮点型:
0
- 字符串型:空字符串
""
- 数组:空数组
array()
依赖未初始化变量的默认值在某些情况下会有问题。
一个典型的例子是,如果两个不同的脚本文件都声明了同名的全局变量,但在一个文件中该变量被初始化而在另一个文件中未被初始化,当这两个文件被包含在同一运行环境中时,未初始化变量的默认值可能会掩盖预期的逻辑错误。
PHP 8.0.0 之前的版本中,访问未初始化的普通变量会触发 E_NOTICE
级别的错误,而从 PHP 8.0.0 开始,这种行为升级为 E_WARNING
,意味着它被视为更严重的错误,可能需要开发者立即关注和修正。但是,向未初始化的数组变量追加元素(例如,使用$arr[] = 'value'
)不会引发错误,因为这会隐式地初始化数组。
为了避免因依赖未初始化变量而引起的错误,最佳实践是使用isset()
函数来检查变量是否已经被定义和初始化。isset()
函数会检查一个或多个变量是否存在且非NULL
,如果变量存在且其值不是NULL
,则返回true
,否则返回false
。
预定义变量
PHP 中,有一系列特殊的变量被称为预定义变量。这些变量由 PHP 环境本身提供,旨在让开发者能够方便地访问到与运行环境、服务器配置以及用户交互相关的数据。不过,预定义变量的具体内容和可用性可能会受到服务器版本、配置以及其他外部条件的影响。部分预定义变量在 PHP 以命令行脚本执行模式下可能不会生效。想要深入了解每种预定义变量的具体用途和限制,建议查阅 PHP 官方文档中的“预定义变量”章节。
自动全局变量(Autoglobals/Superglobals)
除了基本的预定义变量外,PHP 还引入了一组特殊的预定义数组,这些数组被统称为自动全局变量或超全局变量。这些超全局数组自动填充了来自 Web 服务器(如果有)、当前执行环境以及用户提交的信息。它们的特殊之处在于,无论在代码的哪个作用域内,你都可以直接访问这些变量,无需进行显式地全局声明。常见的超全局变量包括$_GET
, $_POST
, $_SESSION
, $_COOKIE
, 等等。要注意的是,PHP 并不支持用户自定义超全局变量。关于所有超全局变量的详尽列表和使用方法,请参考 PHP 文档中的“超全局变量列表”。
注意事项
在使用过程中,有一点需要特别留意:尽管 PHP 允许创建所谓的“可变变量”,即变量的名称可以动态改变,但这种机制并不适用于超全局变量。意味着你不能在函数或类的方法内部,通过可变变量的方式来间接引用超全局变量。例如,试图用 $varName = 'POST'; echo $$varName['key'];
来访问$_POST['key']
是不可行的。
variables_order 配置的影响
PHP 的variables_order
配置项决定了$_GET
, $_POST
, $_COOKIE
, 和 $_FILES
等预定义数组如何根据请求中的数据进行初始化。如果在variables_order
设置中没有包含某个特定的输入类型(如G
, P
, C
, F
代表GET
, POST
, COOKIE
, FILES
),相应的预定义数组将会是空的。
变量范围
“变量范围”是指变量能够被访问和识别的有效区域,通俗来讲就是变量在哪里起作用。通常情况下,PHP的变量遵循一个简单的规则来确定它们的作用域:
基础范围概念
大多数 PHP 变量拥有一个全局作用域,这意味着从它们被定义的地方开始,一直到包含该定义文件的整个脚本结束,包括通过include
或require
命令加载的其他文件里,这些变量都是可以被访问的。比如下面的例子:
<?php
// 这里定义了一个全局变量 $a
$a = 1;
include 'b.inc'; // 在b.inc文件中也能访问到 $a
?>
在这个例子中,变量 $a
被定义后,在被包含的 b.inc
文件里也是可以使用的,因为它处于全局作用域内。
函数内的局部作用域
当涉及到用户自定义函数时,情况稍微复杂一点。每个函数都创建了自己的局部作用域,这意味着在函数内部定义的变量默认只在这个函数内部可见。如果在函数里不特别声明,你是无法直接访问到外部(全局)作用域的变量的。看下面的代码示例:
<?php
$a = 1; // 这是在全局作用域定义的 $a
function Test() {
echo $a; // 尝试访问 $a,但这是错误的,因为这里有一个局部的 $a 存在(尽管它还没被定义)
}
Test();
?>
上面的脚本尝试在Test
函数内部打印变量 $a
,但由于没有明确指定其为全局变量,PHP 会认为这是一个未定义的局部变量,从而引发一个警告(在PHP 8.0.0之前是通知)。为了避免这个问题,需要使用global
关键字来显式地声明你想在函数内部使用的是全局变量:
function Test() {
global $a; // 明确告诉PHP我们要使用的是全局的 $a
echo $a; // 现在可以正确输出全局的 $a 的值了
}
PHP与C语言的差异
不同于 C 语言,PHP 中函数并不会自动继承全局变量。在 C 中,全局变量在函数内部自动可用,除非被同名的局部变量覆盖。而 PHP 设计成这样是为了减少意外修改全局状态的风险,鼓励更清晰的代码结构和更好的编程实践。因此,在 PHP 中,如果你想在函数内部修改或访问全局变量,必须使用global
关键字或者通过$GLOBALS
数组来显式访问,以此来增强代码的可读性和可维护性。
global 关键字
global
关键字用于在函数内部访问和操作全局作用域中的变量。
使用global
关键字的示例:
$a = 1; // 全局变量$a
$b = 2; // 全局变量$b
function Sum() {
global $a, $b; // 使用global关键字引入$a和$b到函数内部作用域
$b = $a + $b; // 修改的是全局的$b
}
Sum(); // 调用函数
echo $b; // 输出$b的值,现在是3因为函数内将其加1了
此脚本展示了如何利用global
关键字使函数内部能够直接修改外部定义的全局变量 $a
和 $b
。执行Sum()
函数后,变量$b
的值变为3,这是由于函数内对全局$a
和$b
进行了相加操作。
使用$GLOBALS
数组作为替代方案
除了global
关键字,PHP还提供了$GLOBALS
超全局数组,它存储了所有全局变量,允许以关联数组的形式访问这些变量。
使用$GLOBALS
的示例:
$a = 1;
$b = 2;
function Sum() {
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b']; // 直接通过$GLOBALS数组访问并修改全局变量
}
Sum();
echo $b; // 同样输出3
在这个版本中,我们没有使用global
关键字,而是通过$GLOBALS
数组的键(即变量名)来访问和修改$a
和$b
的值,效果与使用global
相同。
超全局变量与作用域
超全局变量,如$GLOBALS
、$_POST
、$_GET
等,在脚本的任何地方都可以无需特殊声明直接访问。这里通过一个额外的示例说明超全局变量的作用:
function test_superglobal() {
echo $_POST['name']; // 访问超全局变量$_POST中的'name'键,常用于处理表单提交的数据
}
在这个示例中,尽管$_POST
是在函数内部被访问的,但因为它是一个超全局变量,所以可以直接获取到外部提交过来的表单数据(假设是通过POST方法)。
使用静态变量
静态变量(static variable),它允许你在函数内部定义一个变量,其生命周期超越了该函数的单次调用。即使函数执行结束,静态变量的值仍会被保留,供下一次函数调用使用。
静态变量的基本概念
静态变量的特别之处在于它们具有“记忆”功能。尽管它们像局部变量一样只在函数内部定义,但不同的是,当函数执行完毕后,静态变量并不会被销毁,其值会保留下来,直到脚本结束或被显式改变。
演示需要静态变量的例子
原始示例:
function Test() {
$a = 0;
echo $a;
$a++;
}
这个函数每次调用时都会重置$a
为0并打印0,因为$a
是局部变量,函数执行结束后其值就会丢失。
改进方案:
通过将$a
声明为静态变量,我们能实现计数的功能,因为它在函数多次调用间维持状态。
function test() {
static $a = 0; // 使用'static'关键字声明$a为静态变量
echo $a;
$a++; // 这次$a的增加会在下一次调用时保留
}
静态变量在递归函数中的应用
递归函数是指在函数内部调用自身的函数。使用静态变量可以有效地控制递归,防止无限循环。例如,下面的函数通过静态变量$count
来追踪递归深度,当达到指定次数(如10次)时停止递归。
function recursiveCount() {
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
recursiveCount(); // 递归调用自身
}
}
静态变量的初始化限制
静态变量只能用常量表达式初始化。尝试用函数调用等动态表达式初始化静态变量会导致错误。
function example() {
static $value = 3 * 4; // 正确,常量表达式
static $value = rand(1, 10); // 错误,因为rand()是函数调用
}
PHP 8.1.0 及以后版本中的静态变量行为变化
自PHP 8.1.0起,如果子类继承了一个包含静态变量的方法而不是覆盖它,那么该静态变量会在父类和子类之间共享,类似于静态属性的行为。
class ParentClass {
public static function counter() {
static $counter = 0;
$counter++;
return $counter;
}
}
class ChildClass extends ParentClass {}
// 调用证明了静态变量在父子类间共享
echo ParentClass::counter(); // 输出: 1
echo ParentClass::counter(); // 输出: 2
echo ChildClass::counter(); // 输出: 3 (在PHP 8.1.0之前可能是1)
echo ChildClass::counter(); // 输出: 4 (在PHP 8.1.0之前可能是2)
注意事项
静态变量的声明和初始化是在编译阶段完成的,这意味着它们的值和存在性在程序运行前就已经确定。
可变变量
可变变量是一种灵活的特性,它允许你动态地创建或引用变量名。你可以在运行时设定和使用变量的名称,为程序提供了更高的灵活性。
基础概念
通常,我们定义一个变量是这样的:
$a = 'hello';
这里,$a
是一个存储着字符串’hello’的普通变量。
可变变量的引入
但是,如果想让变量名本身也是动态的,就可以使用可变变量。比如,你想基于$a
的值创建或修改另一个变量,可以这样做:
$$a = 'world';
在这个例子中,因为$a
的值是’hello’,所以实际上创建了一个名为$hello
的新变量,并将其值设为’world’。
输出可变变量
于是,当你尝试输出这些变量时,会有如下效果:
echo "$a {$$a}";
这行代码会输出hello world
,因为它首先输出$a
的值(’hello’),然后输出$hello
的值(’world’)。
解决数组中的歧义
当在数组上下文中使用可变变量时,为了消除解析上的模糊性,你需要使用大括号 {}
来明确指示。例如,要访问数组元素作为可变变量名,应这样写:
${$a[1]} // 如果$a是一个包含变量名的数组
${$a}[1] // 如果想要的是变量$a指向的数组中的第二个元素
可变属性的使用
可变变量同样适用于对象的属性访问。例如,如果你有一个类foo
,你可以动态地引用它的属性:
class foo {
public $bar = 'I am bar.';
// ...其他属性
}
$foo = new foo();
$bar = 'bar';
echo $foo->$bar; // 输出 "I am bar."
这里,$foo->$bar
实际上是访问了$foo
对象的$bar
属性。
使用花括号清晰界定属性名
特别是当属性名复杂(如数组索引、多部分名称或含有特殊字符)时,花括号提供了一种清晰的界定方式:
$arr = 'arr';
echo $foo->{$arr}[1]; // 访问$foo的$arr属性(即$arr数组)的第一个元素
注意事项
最后,需要注意的是,在PHP 的函数和类方法内部,不能直接使用超全局变量(如$_GET
, $_POST
等)作为可变变量。同时,$this
关键字,用于引用当前对象实例,也不能被动态引用为可变变量。