学过C的人用PHP的时候一般会相当顺手,而且感到PHP太方便太轻松。但在变量作用域这方面却与C有不同的地方,搞不好会相当郁闷,就找不到错误所在。昨晚就与到这么一个问题,是全局变量在函数中的问题。今天搜索了一下,发现一篇相当不错的文章,讲了PHP中的变量作用域。是一位网友翻译的,在这贴一下:

一、变量范围

变量的范围即它定义的上下文背景(译者:说白了,也就是它的生效范围)。大部分的 PHP 变量只有一个单独的范围。这个单独的范围跨度同样包含了 include 和 require 引入的文件。范例:

  1. <?php
  2. $a = 1;
  3. include "b.inc";
  4. ?>

这里变量 $a 将会在包含文件 b.inc 中生效。但是,在用户自定义函数中,一个局部函数范围将被引入。任何用于函数内部的变量按缺省情况将被限制在局部函数范围内。范例:

  1. <?php
  2. $a = 1; /* global scope */
  3.  
  4. function Test() {
  5. echo $a; /* reference to local scope variable */
  6. }
  7.  
  8. Test();
  9. ?>

这个脚本不会有任何输出,因为 echo 语句引用了一个局部版本的变量 $a,而且在这个范围内,它并没有被赋值。你可能注意到 PHP 的全局变量和 C 语言有一点点不同,在 C 语言中,全局变量在函数中自动生效,除非被局部变量覆盖。这可能引起一些问题,有些人可能漫不经心的改变一个全局变量。PHP 中全局变量在函数中使用时必须申明为全局。

二、The global keyword

首先,一个使用 global 的例子:

例子 12-1. 使用 global

  1. <?php
  2. $a = 1;
  3. $b = 2;
  4.  
  5. function Sum() {
  6. global $a, $b;
  7. $b = $a + $b;
  8. }
  9.  
  10. Sum();
  11. echo $b;
  12. ?>

以上脚本的输出将是 "3"。在函数中申明了全局变量 $a$b,任何变量的所有引用变量都会指向到全局变量。对于一个函数能够申明的全局变量的最大个数,PHP 没有限制。

在全局范围内访问变量的第二个办法,是用特殊的 PHP 自定义 $GLOBALS 数组。前面的例子可以写成:

例子 12-2. 使用 $GLOBALS 替代 global

  1. <?php
  2. $a = 1;
  3. $b = 2;
  4.  
  5. function Sum() {
  6. $GLOBALS["b"] = $GLOBALS["a"] + $GLOBALS["b"];
  7. }
  8.  
  9. Sum();
  10. echo $b;
  11. ?>

$GLOBALS 数组中,每一个变量为一个元素,键名对应变量名,值变量的内容。$GLOBALS 之所以在全局范围内存在,是因为 $GLOBALS 是一个超全局变量。以下范例显示了超全局变量的用处:

例子 12-3. 演示超全局变量作用的例子

  1. <?php
  2. function test_global() {
  3.  
  4. // 大多数的预定义变量并不 "super",它们需要用 'global' 关键字来使它们在函数的本地区域中有效。
  5. global $HTTP_POST_VARS;
  6.  
  7. print $HTTP_POST_VARS['name'];
  8.  
  9. // Super globals 在任何范围内都有效,它们并不需要 'global' 声明。Super globals 是在 PHP 4.1.0 引入的。
  10. print $_POST['name'];
  11. }
  12. ?>

三、使用静态变量

变量范围的另一个重要特性是静态变量(static variable)。静态变量仅在局部函数域中存在,但当程序执行离开此作用域时,其值并不丢失。看看下面的例子:

例子 12-4. 演示需要静态变量的例子

  1. <?php
  2. function Test() {
  3. $a = 0;
  4. echo $a;
  5. $a++;
  6. }
  7. ?>

本函数没什么用处,因为每次调用时都会将 $a 的值设为 0 并输出 "0"。将变量加一的 $a++ 没有作用,因为一旦退出本函数则变量 $a 就不存在了。要写一个不会丢失本次计数值的计数函数,要将变量 $a 定义为静态的:

例子 12-5. 使用静态变量的例子

  1. <?php
  2. function Test() {
  3. static $a = 0;
  4. echo $a;
  5. $a++;
  6. }
  7. ?>

现在,每次调用 Test() 函数都会输出 $a 的值并加一。

静态变量也提供了一种处理递归函数的方法。递归函数是一种调用自己的函数。写递归函数时要小心,因为可能会无穷递归下去。必须确保有充分的方法来中止递归。以下这个简单的函数递归计数到 10,使用静态变量 $count 来判断何时停止:

例子 12-6. 静态变量与递归函数

  1. <?php
  2. function Test() {
  3. static $count = 0;
  4.  
  5. $count++;
  6. echo $count;
  7. if ($count < 10) {
  8. Test ();
  9. }
  10. }
  11. ?>

注意: 静态变量可以按照上面的例子声明。如果在声明中用“表达式的结果”对其赋值会导致解析错误。

例子 12-7. 声明静态变量

  1. <?php
  2. function foo() {
  3. static $int = 0; // correct
  4. static $int = 1+2; // wrong (as it is an expression)
  5. static $int = sqrt(121); // wrong (as it is an expression too)
  6.  
  7. $int++;
  8. echo $int;
  9. }
  10. ?>

四、全局和静态变量的引用

在 Zend 引擎 1 代,驱动了 PHP4,对于变量的 static 和 global 定义是以 references 的方式实现的。例如,在一个函数域内部用 global 语句导入的一个真正的全局变量实际上是建立了一个到全局变量的引用。这有可能导致预料之外的行为,如以下例子所演示的:

  1. <?php
  2. function test_global_ref() {
  3. global $obj;
  4. $obj = &new stdclass;
  5. }
  6.  
  7. function test_global_noref() {
  8. global $obj;
  9. $obj = new stdclass;
  10. }
  11.  
  12. test_global_ref();
  13. var_dump($obj);
  14. test_global_noref();
  15. var_dump($obj);
  16. ?>

执行以上例子会导致如下输出:

  1. NULL
  2. object(stdClass)(0) {
  3.  
  4. }

类似的行为也适用于 static 语句。引用并不是静态地存储的

  1. <?php
  2. function &get_instance_ref() {
  3. static $obj;
  4.  
  5. echo "Static object: ";
  6. var_dump($obj);
  7. if (!isset($obj)) {
  8. // 将一个引用赋值给静态变量
  9. $obj = &new stdclass;
  10. }
  11. $obj->property++;
  12.  
  13. return $obj;
  14. }
  15.  
  16. function &get_instance_noref() {
  17. static $obj;
  18.  
  19. echo "Static object: ";
  20. var_dump($obj);
  21. if (!isset($obj)) {
  22. // 将一个对象赋值给静态变量
  23. $obj = new stdclass;
  24. }
  25. $obj->property++;
  26.  
  27. return $obj;
  28. }
  29.  
  30. $obj1 = get_instance_ref();
  31. $still_obj1 = get_instance_ref();
  32. echo "<br/>";
  33. $obj2 = get_instance_noref();
  34. $still_obj2 = get_instance_noref();
  35. ?>

执行以上例子会导致如下输出:

  1. Static object: NULL
  2. Static object: NULL
  3.  
  4. Static object: NULL
  5. Static object: object(stdClass)(1) {
  6. ["property"]=>
  7. int(1)
  8. }

上例演示了当把一个引用赋值给一个静态变量时,第二次调用 &get_instance_ref() 函数时其值并没有被记住。

附: PHP常量、变量作用域详解(一)

PHP变量作用域详解(二)的更多相关文章

  1. PHP常量、变量作用域详解(一)

    PHP 中的每个变量都有一个针对它的作用域,它是指可以在其中访问变量(从而访问它的值)的一个领域.对于初学者来说,变量的作用域是它们所驻留的页面.因此, 如果你定义了 $var,页面余下部分就可以访问 ...

  2. JavaScript 变量作用域 详解

    变量作用域要点 - 在JavaScript中没有块级作用域,只有函数作用域 - 在函数体内,局部变量的优先级高于同名的全局变量 - 在全局作用域编写代码时可以不写var语句,但声明局部变量时必须使用v ...

  3. (转载)Java变量作用域详解

    转载自http://www.cnblogs.com/AlanLee/p/6627949.html 大多数程序设计语言都提供了"作用域"(Scope)的概念. 对于在作用域里定义的名 ...

  4. python3 之 变量作用域详解

    作用域: 指命名空间可直接访问的python程序的文本区域,这里的 ‘可直接访问’ 意味着:对名称的引用(非限定),会尝试在命名空间中查找名称: L:local,局部作用域,即函数中定义的变量: E: ...

  5. Java基础-变量的定义以及作用域详解

    Java基础-变量的定义以及作用域详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.字面量 常量(字面量)表示不能改变的数值(程序中直接出现的值).字面量有时也称为直接量,包 ...

  6. Python中的变量和作用域详解

    Python中的变量和作用域详解 python中的作用域分4种情况: L:local,局部作用域,即函数中定义的变量: E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部 ...

  7. PopUpWindow使用详解(二)——进阶及答疑

      相关文章:1.<PopUpWindow使用详解(一)——基本使用>2.<PopUpWindow使用详解(二)——进阶及答疑> 上篇为大家基本讲述了有关PopupWindow ...

  8. “全栈2019”Java异常第六章:finally代码块作用域详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  9. “全栈2019”Java异常第四章:catch代码块作用域详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

随机推荐

  1. 基于移动端Reactive Native轮播组件的应用与开发详解

    总结下这段时间学习reactive native的一些东西,我们来认识一下,被炒得这么火的rn,究竟是个什么东西,以及如何去搭建自己的demo. reactive  native是什么 由facebo ...

  2. [No000074]C#创建桌面快捷方式

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  3. python基础之编码问题

    python基础之编码问题 本节内容 字符串编码问题由来 字符串编码解决方案 1.字符串编码问题由来 由于字符串编码是从ascii--->unicode--->utf-8(utf-16和u ...

  4. 初学C#和MVC的一些心得,弯路,总结,还有教训(1)--语言的选择

    因为惰性,自制力,求知欲等各方面原因....一直没有学新技术,总感觉VB6凑合能用就凑合用.... 于是大概从05年开始,几乎每次新版的vs一发布,我就下载回来,然后安装,然后,,,,就扔那了.... ...

  5. .net Core学习笔记:Windows环境搭建

    1.安装 VS2015 Update3.如果已经安装了VS2015,但不是Update3版本,请在VS的工具 --> 扩展与更新 中执行update3的升级(大约需要2小时). 2..net C ...

  6. 如何理解vue.js组件的作用域是独立的

    vue.js组件的作用域是独立,可以从以下三个方面理解: 1.父组件模板在父组件作用域内编译,父组件模板的数据用父组件内data数据:2.子组件模板在子组件作用域内编译,子组件模板的数据用子组件内da ...

  7. 51Nod-1279 扔盘子

    51Nod:  http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1279 1279 扔盘子 题目来源: Codility 基 ...

  8. javascript 中断函数的使用 setInterval()——返回顶部

    方法名称:gotop() 功能描述:点击某个元素,调用方法gotop(),固定间隔,滚动至屏幕顶部 日期 :2016.06.06 16:02 author  :cyh2009@live.com < ...

  9. kuangbin专题总结一 简单搜索

    A - 棋盘问题:在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有 ...

  10. struts2报错:There is no Action mapped for namespace [/] and action name [userAction!add]

    使用struts2.3进行动态方法调用时出现: There is no Action mapped for namespace [/user] and action name [user!add] a ...