一、基础
PHP 中的变量用一个美元符号后面跟变量名来表示。变量名是区分大小写的。变量名与 PHP 中其它的标签一样遵循相同的规则。一个有效的变量名由字母或者下划线开头,后面跟上任意数量的字母,数字,或者下划线。 按照正常的正则表达式,它将被表述为:’^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$’。
注意:
- 在此所说的字母是 a-z,A-Z,以及 ASCII 字符从 128 到 255(0x80-0xff)。
- $this 是一个特殊的变量,它不能被赋值。 PHP 7.1.0 之前,间接赋值(例如通过使用可变变量)是可能的。
<?php $var = 'Bob'; $Var = 'Joe'; echo "$var, $Var"; // 输出 "Bob, Joe" $4site = 'not yet'; // 非法变量名;以数字开头 $_4site = 'not yet'; // 合法变量名;以下划线开头 $i站点is = 'mansikka'; // 合法变量名;可以用中文 ?>
变量默认总是传值赋值。那也就是说,当将一个表达式的值赋予一个变量时,整个原始表达式的值被赋值到目标变量。例如,当一个变量的值赋予另外一个变量时,改变其中一个变量的值,将不会影响到另外一个变量。
PHP 也提供了另外一种方式给变量赋值:引用赋值。表示新的变量简单的引用(换言之,“成为其别名” 或者 “指向”)了原始变量,改动新的变量将影响到原始变量,反之亦然。使用引用赋值,简单地将一个 & 符号加到将要赋值的变量前(源变量)。例如,下列代码片断将输出“My name is Bob”两次:
<?php $foo = 'Bob'; // 将 'Bob' 赋给 $foo $bar = &$foo; // 通过 $bar 引用 $foo $bar = "My name is $bar"; // 修改 $bar 变量 echo $bar; echo $foo; // $foo 的值也被修改 ?>
只有有名字的变量才可以引用赋值。
<?php $foo = 25; $bar = &$foo; // 合法的赋值 $bar = &(24 * 7); // 非法; 引用没有名字的表达式 function test() { return 25; } $bar = &test(); // 非法 ?>
虽然在 PHP 中并不需要初始化变量,但对变量进行初始化是个好习惯。未初始化的变量具有其类型的默认值 – 布尔类型的变量默认值是 false,整形和浮点型变量默认值是零,字符串型变量(例如用于 echo 中)默认值是空字符串以及数组变量的默认值是空数组。
示例 :未初始化变量的默认值
<?php // 未设置和未引用(不使用上下文)的变量;输出 NULL var_dump($unset_var); // Boolean 用法;输出 'false' (See ternary operators for more on this syntax) echo $unset_bool ? "true\n" : "false\n"; // String 用法;输出 'string(3) "abc"' $unset_str .= 'abc'; var_dump($unset_str); // Integer 用法;输出 'int(25)' $unset_int += 25; // 0 + 25 => 25 var_dump($unset_int); // Float 用法;输出 'float(1.25)' $unset_float += 1.25; var_dump($unset_float); // Array 用法;输出 array(1) { [3]=> string(3) "def" } $unset_arr[3] = "def"; // array() + array(3 => "def") => array(3 => "def") var_dump($unset_arr); // Object 用法:创建新 stdClass 对象 (see http://www.php.net/manual/en/reserved.classes.php) // Outputs: object(stdClass)#1 (1) { ["foo"]=> string(3) "bar" } $unset_obj->foo = 'bar'; var_dump($unset_obj); ?>
依赖未初始化变量的默认值在某些情况下会有问题,例如把一个文件包含到另一个之中时碰上相同的变量名。 使用未初始化的变量会发出 E_WARNING (在 PHP 8.0.0 之前是 E_NOTICE) 错误,但是在向一个未初始化的数组附加单元时不会。isset() 语言结构可以用来检测一个变量是否已被初始化。
二、预定义变量
PHP 提供了大量的预定义变量,由于许多变量依赖于运行的服务器的版本和设置,及其它因素,所以并没有详细说明。一些预定义变量在 PHP 以命令行形式运行时并不生效。
PHP 提供了一套附加的预定数组,这些数组变量包含了来自 web 服务器(如果可用),运行环境和用户输入的数据。这些数组非常特别,它们在全局范围内自动生效,例如,在任何范围内自动生效。因此通常被称为自动全局变量(autoglobals)或者超全局变量(superglobals)。(PHP 中没有用户自定义超全局变量的机制。)
注意:
- 超级全局变量不能被用作函数或类方法中的可变变量。
- 如果某些 variables_order 中的变量没有设定,它们的对应的 PHP 预定义数组也是空的。
三、变量范围
变量的范围即它定义的上下文背景(也就是它的生效范围)。大部分的 PHP 变量只有一个单独的范围。这个单独的范围跨度同样包含了 include 和 require 引入的文件。例如:
<?php $a = 1; include 'b.inc'; ?>
这里变量 $a 将会在包含文件 b.inc 中生效。但是,在用户自定义函数中,一个局部函数范围将被引入。任何用于函数内部的变量按缺省情况将被限制在局部函数范围内。例如:
<?php $a = 1; /* global scope */ function Test() { echo $a; /* reference to local scope variable */ } Test(); ?>
这个脚本会生成未定义变量 E_WARNING(PHP 8.0.0 之前是 E_NOTICE)诊断提示。然而,如果 display_errors INI 设置为隐藏这类诊断提示,然后在任何情况下都不会有输出,这是因为 echo 语句引用了一个局部版本的变量 $a,而且在这个范围内,它并没有被赋值。你可能注意到 PHP 的全局变量和 C 语言有一点点不同,在 C 语言中,全局变量在函数中自动生效,除非被局部变量覆盖。这可能引起一些问题,有些人可能不小心就改变了一个全局变量。PHP 中全局变量在函数中使用时必须声明为 global。
1、global关键字
首先,一个使用 global 的例子:
示例 :使用global
<?php $a = 1; $b = 2; function Sum() { global $a, $b; $b = $a + $b; } Sum(); echo $b; ?>
以上脚本的输出将是“3”。在函数中声明了全局变量 $a 和 $b 之后,对任一变量的所有引用都会指向其全局版本。对于一个函数能够声明的全局变量的最大个数,PHP 没有限制。
在全局范围内访问变量的第二个办法,是用特殊的 PHP 自定义 $GLOBALS 数组。前面的例子可以写成:
示例:使用$GLOBALS替代global
<?php $a = 1; $b = 2; function Sum() { $GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b']; } Sum(); echo $b; ?>
$GLOBALS 是一个关联数组,每一个变量为一个元素,键名对应变量名,值对应变量的内容。$GLOBALS 之所以在全局范围内存在,是因为 $GLOBALS 是一个超全局变量。以下范例显示了超全局变量的用处:
示例 :演示超全局变量和作用域的例子
<?php function test_superglobal() { echo $_POST['name']; } ?>
2、使用静态变量
变量范围的另一个重要特性是静态变量(static variable)。静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。看看下面的例子:
示例:需要静态变量
<?php function Test() { $a = 0; echo $a; $a++; } ?>
本函数没什么用处,因为每次调用时都会将 $a 的值设为 0 并输出 0。将变量加一的 $a++ 没有作用,因为一旦退出本函数则变量 $a 就不存在了。要写一个不会丢失本次计数值的计数函数,要将变量 $a 定义为静态的:
示例:使用静态变量
<?php function test() { static $a = 0; echo $a; $a++; } ?>
现在,变量 $a 仅在第一次调用 test() 函数时被初始化,之后每次调用 test() 函数都会输出 $a 的值并加一。
静态变量也提供了一种处理递归函数的方法。递归函数是一种调用自己的函数。写递归函数时要小心,因为可能会无穷递归下去。必须确保有充分的方法来中止递归。以下这个简单的函数递归计数到 10,使用静态变量 $count 来判断何时停止:
示例:静态变量与递归函数
<?php function test() { static $count = 0; $count++; echo $count; if ($count < 10) { test(); } $count--; } ?>
常量表达式的结果可以赋值给静态变量,但是动态表达式(比如函数调用)会导致解析错误。
示例:声明静态变量
<?php function foo(){ static $int = 0; // 正确 static $int = 1+2; // 正确 static $int = sqrt(121); // 错误(因为它是函数) $int++; echo $int; } ?>
从 PHP 8.1.0 开始,当继承(不是覆盖)使用有静态变量的方法时,继承的方法将会跟父级方法共享静态变量。这意味着方法中的静态变量现在跟静态属性有相同的行为。
示例:在继承方法中使用静态变量
<?php class Foo { public static function counter() { static $counter = 0; $counter++; return $counter; } } class Bar extends Foo {} var_dump(Foo::counter()); // int(1) var_dump(Foo::counter()); // int(2) var_dump(Bar::counter()); // int(3),PHP 8.1.0 之前 int(1) var_dump(Bar::counter()); // int(4),PHP 8.1.0 之前 int(2) ?>
注意:静态声明在编译时解析。
3、全局和静态变量引用
对于变量的 static 和 global 定义是以引用的方式实现的。例如,在一个函数域内部用 global 语句导入的一个真正的全局变量实际上是建立了一个到全局变量的引用。这有可能导致预料之外的行为,如以下例子所演示的:
<?php function test_global_ref() { global $obj; $new = new stdClass; $obj = &$new; } function test_global_noref() { global $obj; $new = new stdClass; $obj = $new; } test_global_ref(); var_dump($obj); test_global_noref(); var_dump($obj); ?>
以上示例会输出:
NULL object(stdClass)#1 (0) { }
类似的行为也适用于 static 语句。引用并不是静态地存储的:
<?php function &get_instance_ref() { static $obj; echo 'Static object: '; var_dump($obj); if (!isset($obj)) { $new = new stdClass; // 将一个引用赋值给静态变量 $obj = &$new; } if (!isset($obj->property)) { $obj->property = 1; } else { $obj->property++; } return $obj; } function &get_instance_noref() { static $obj; echo 'Static object: '; var_dump($obj); if (!isset($obj)) { $new = new stdClass; // 将一个对象赋值给静态变量 $obj = $new; } if (!isset($obj->property)) { $obj->property = 1; } else { $obj->property++; } return $obj; } $obj1 = get_instance_ref(); $still_obj1 = get_instance_ref(); echo "\n"; $obj2 = get_instance_noref(); $still_obj2 = get_instance_noref(); ?>
以上示例会输出:
Static object: NULL Static object: NULL Static object: NULL Static object: object(stdClass)#3 (1) { ["property"]=> int(1) }
上例演示了当把一个引用赋值给一个静态变量时,第二次调用 &get_instance_ref() 函数时其值并没有被记住。
四、可变变量
有时候使用可变变量名是很方便的。就是说,一个变量的变量名可以动态的设置和使用。一个普通的变量通过声明来设置,例如:
<?php $a = 'hello'; ?>
一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。在上面的例子中 hello 使用了两个美元符号($)以后,就可以作为一个可变变量的变量了。例如:
<?php $$a = 'world'; ?>
这时,两个变量都被定义了:$a 的内容是“hello”并且 $hello 的内容是“world”。因此,以下语句:
<?php echo "$a {$$a}"; ?>
与以下语句输出完全相同的结果:
<?php echo "$a $hello"; ?>
它们都会输出:hello world。
要将可变变量用于数组,当我们写下 $$a[1] 时,解析器需要确定我们是想使用 $a[1] 作为一个变量,还是想使用 $$a 作为一个变量并取出该变量中索引为 [1] 的值。为了解决这个问题,我们可以使用 {a[1]} 表示第一种情况,即将 $a[1] 视为一个变量;而使用 {a}[1] 则表示第二种情况,即将 $$a 视为一个变量,并获取该变量中索引为 [1] 的值。
类的属性也可以通过可变属性名来访问。可变属性名将在该调用所处的范围内被解析。例如,对于 $foo->$bar 表达式,则会在本地范围来解析 $bar 并且其值将被用于 $foo 的属性名。对于 $bar 是数组单元时也是一样。
也可使用花括号来给属性名清晰定界。最有用是在属性位于数组中,或者属性名包含有多个部分或者属性名包含有非法字符时(例如来自 json_decode() 或 SimpleXML)。
示例 :可变属性示例
<?php class foo { var $bar = 'I am bar.'; var $arr = array('I am A.', 'I am B.', 'I am C.'); var $r = 'I am r.'; } $foo = new foo(); $bar = 'bar'; $baz = array('foo', 'bar', 'baz', 'quux'); echo $foo->$bar . "\n"; echo $foo->{$baz[1]} . "\n"; $start = 'b'; $end = 'ar'; echo $foo->{$start . $end} . "\n"; $arr = 'arr'; echo $foo->{$arr[1]} . "\n"; ?>
以上示例会输出:
I am bar. I am bar. I am bar. I am r.
注意,在 PHP 的函数和类的方法中,超全局变量不能用作可变变量。$this 变量也是一个特殊变量,不能被动态引用。
五、PHP 之外的变量
1、HTML 表单
当一个表单(GET 和 POST)提交给 PHP 脚本时,表单中的信息会自动在脚本中可用。有几个方法访问此信息,例如:
示例:一个简单的 HTML 表单
<form action="foo.php" method="POST"> Name: <input type="text" name="username"><br /> Email: <input type="text" name="email"><br /> <input type="submit" name="submit" value="Submit me!" /> </form>
只有两种方法可以访问 HTML 表单中的数据。 以下列出了当前有效的方法:
示例:从一个简单的 POST HTML 表单访问数据
<?php echo $_POST['username']; echo $_REQUEST['username']; ?>
使用 GET 表单也类似,只不过要用适当的 GET 预定义变量。GET 也适用于 QUERY_STRING(URL 中在“?”之后的信息)。因此,举例说,http://www.example.com/test.php?id=3 包含有可用 $_GET[‘id’] 来访问的 GET 数据。
注意:
变量名中的点和空格被转换成下划线。例如 <input name=”a.b” /> 变成了 $_REQUEST[“a_b”]。
PHP 也懂得表单变量上下文中的数组。例如可以将相关的变量编成组,或者用此特性从多选输入框中取得值。例如,将一个表单 POST 给自己并在提交时显示数据:
示例 :更复杂的表单变量
<?php if (isset($_POST['action']) && $_POST['action'] == 'submitted') { echo '<pre>'; print_r($_POST); echo '<a href="'. $_SERVER['PHP_SELF'] .'" rel="external nofollow" >Please try again</a>'; echo '</pre>'; } else { ?> <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post"> Name: <input type="text" name="personal[name]"><br /> Email: <input type="text" name="personal[email]"><br /> Beer: <br> <select multiple name="beer[]"> <option value="warthog">Warthog</option> <option value="guinness">Guinness</option> <option value="stuttgarter">Stuttgarter Schwabenbr</option> </select><br /> <input type="hidden" name="action" value="submitted" /> <input type="submit" name="submit" value="submit me!" /> </form> <?php } ?>
注意:如果一个外部变量名以有效的数组语法开始,后续的字符将会被静默忽略。例如,<input name=”foo[bar]baz”> 会被解析为 $_REQUEST[‘foo’][‘bar’]。
2、IMAGE SUBMIT变量名
当提交表单时,可以用一幅图像代替标准的提交按钮,用类似这样的标记:
<input type="image" src="image.gif" name="sub" />
当用户点击到图像中的某处时,相应的表单会被传送到服务器,并加上两个变量 sub_x 和 sub_y。它们包含了用户点击图像的坐标。有经验的用户可能会注意到被浏览器发送的实际变量名包含的是一个点而不是下划线(即 sub.x 和 sub.y),但 PHP 自动将点转换成了下划线。
3、HTTP Cookies
PHP 透明地支持 RFC 6265定义中的 HTTP cookies。Cookies 是一种在远端浏览器端存储数据并能追踪或识别再次访问的用户的机制。可以用 setcookie() 函数设定 cookies。Cookies 是 HTTP 信息头中的一部分,因此 SetCookie 函数必须在向浏览器发送任何输出之前调用。对于 header() 函数也有同样的限制。Cookie 数据会在相应的 cookie 数据数组中可用,例如 $_COOKIE 和 $_REQUEST。
注意: 从 PHP 版本 7.2.34、7.3.23 和 7.4.11 开始,出于安全考虑,传入的 cookie 名称不再进行 URL 解码。
如果要将多个值赋给一个 cookie 变量,必须将其赋成数组。例如:
<?php setcookie("MyCookie[foo]", 'Testing 1', time()+3600); setcookie("MyCookie[bar]", 'Testing 2', time()+3600); ?>
这将会建立两个单独的 cookie,尽管 MyCookie 在脚本中是一个单一的数组。如果想在仅仅一个 cookie 中设定多个值,考虑先在值上使用 serialize() 或 explode()。
注意在浏览器中一个 cookie 会替换掉上一个同名的 cookie,除非路径或者域不同。因此对于购物车程序可以保留一个计数器并一起传递,例如:
示例 : 一个 setcookie() 的示例
<?php if (isset($_COOKIE['count'])) { $count = $_COOKIE['count'] + 1; } else { $count = 1; } setcookie('count', $count, time()+3600); setcookie("Cart[$count]", $item, time()+3600); ?>
4、变量名中的点
通常,PHP 不会改变传递给脚本中的变量名。然而应该注意到点(句号)不是 PHP 变量名中的合法字符。至于原因,看看:
<?php $varname.ext; /* 非法变量名 */ ?>
这时,解析器看到是一个名为 $varname 的变量,后面跟着一个字符串连接运算符,后面跟着一个裸字符串(即没有加引号的字符串,且不匹配任何已知的健名或保留字)’ext’。很明显这不是想要的结果。出于此原因,要注意 PHP 将会自动将变量名中的点替换成下划线。
5、确定变量类型
因为 PHP 会判断变量类型并在需要时进行转换(通常情况下),因此在某一时刻给定的变量是何种类型并不明显。PHP 包括几个函数可以判断变量的类型,例如:gettype(),is_array(),is_float(),is_int(),is_object() 和 is_string()。
由于 HTTP 是一种文本协议,大部分甚至所有以超全局数组(如 $_POST 和 _GET)形式传递的内容都将保留为字符串。PHP 不会尝试将值转换为特定类型。在下面的示例中, GET)形式传递的内容都将保留为字符串。PHP不会尝试将值转换为特定类型。在下面的示例中,_GET[“var1”] 将包含字符串 “null”,而 $_GET[“var2”] 将包含字符串 “123”。
/index.php?var1=null&var2=123
6、更新日志