PHP 变量

预计阅读时间3 分钟 165 views

变量基础

在 PHP 中,变量命名需要遵循一套既定的规则,这些规则与 PHP 中其他类型的标识符或标签保持一致。一个正确的变量名应当满足以下条件:

  1. 起始字符:变量名必须以字母(小写 a-z 或大写 A-Z)或者下划线 _ 开头。所以,数字不能作为变量名的首字符。
  2. 后续字符:首字符之后,可以跟随任意数量的字母(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 变量拥有一个全局作用域,这意味着从它们被定义的地方开始,一直到包含该定义文件的整个脚本结束,包括通过includerequire命令加载的其他文件里,这些变量都是可以被访问的。比如下面的例子:

<?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关键字,用于引用当前对象实例,也不能被动态引用为可变变量。

分享此文档

PHP 变量

或复制链接

本页目录