浅析php变量作用域的一些问题


Posted in PHP onAugust 08, 2013

昨晚就与到这么一个问题,是全局变量在函数中的问题。今天搜索了一下,发现一篇相当不错的文章,讲了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();
?> 

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

The global keyword
首先,一个使用 global 的例子:
例子 12-1. 使用 global

<?php
$a = 1;
$b = 2;
function Sum()
{
   global $a, $b;
   $b = $a + $b;
}
Sum();
echo $b;
?> 

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

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

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

<?php
$a = 1;
$b = 2;
function Sum()
{
   $GLOBALS["b"] = $GLOBALS["a"] + $GLOBALS["b"];
}
Sum();
echo $b;
?> 

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

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

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

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

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

<?php
function Test ()
{
   $a = 0;
   echo $a;
   $a++;
}
?> 

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

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

<?php
function Test()
{
   static $a = 0;
   echo $a;
   $a++;
}
?> 

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

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

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

<?php
function Test()
{
   static $count = 0;
   $count++;
   echo $count;
   if ($count < 10) {
   Test ();
   }
   $count--;
}
?> 

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

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

<?php
function foo(){
   static $int = 0; // correct
   static $int = 1+2; // wrong (as it is an expression)
   static $int = sqrt(121); // wrong (as it is an expression too)
   $int++;
   echo $int;
}
?> 

全局和静态变量的引用
在 Zend 引擎 1 代,驱动了 PHP4,对于变量的 static 和 global 定义是以 references 的方式实现的。例如,在一个函数域内部用 global 语句导入的一个真正的全局变量实际上是建立了一个到全局变量的引用。这有可能导致预料之外的行为,如以下例子所演示的:
<?php
function test_global_ref() {
   global $obj;
   $obj = &new stdclass;
}
function test_global_noref() {
   global $obj;
   $obj = new stdclass;
}
test_global_ref();
var_dump($obj);
test_global_noref();
var_dump($obj);
?> 

执行以上例子会导致如下输出:
NULLobject(stdClass)(0) {}
类似的行为也适用于 static 语句。引用并不是静态地存储的:
<?php
function &get_instance_ref() {
   static $obj;
   echo "Static object: ";
   var_dump($obj);
   if (!isset($obj)) {
   // 将一个引用赋值给静态变量
   $obj = &new stdclass;
   }
   $obj->property++;
   return $obj;
}
function &get_instance_noref() {
   static $obj;
   echo "Static object: ";
   var_dump($obj);
   if (!isset($obj)) {
   // 将一个对象赋值给静态变量
   $obj = new stdclass;
   }
   $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: NULLStatic object: NULLStatic object: NULLStatic object: object(stdClass)(1) {  ["property"]=>  int(1)} 上例演示了当把一个引用赋值给一个静态变量时,第二次调用 &get_instance_ref() 函数时其值并没有被记住。
PHP 相关文章推荐
Classes and Objects in PHP5-面向对象编程 [1]
Oct 09 PHP
php中获取关键词及所属来源搜索引擎名称的代码
Feb 15 PHP
解析PHP将对象转换成数组的方法(兼容多维数组类型)
Jun 21 PHP
php 发送带附件邮件示例
Jan 23 PHP
php版淘宝网查询商品接口代码示例
Jun 17 PHP
php实现删除空目录的方法
Mar 16 PHP
Laravel 5框架学习之日期,Mutator 和 Scope
Apr 08 PHP
举例详解PHP脚本的测试方法
Aug 05 PHP
PHP MSSQL 分页实例
Apr 13 PHP
php数组分页实现方法
Apr 30 PHP
Yii2分页的使用及其扩展方法详解
May 23 PHP
zend框架实现支持sql server的操作方法
Dec 08 PHP
解析php开发中的中文编码问题
Aug 08 #PHP
php中jpgraph类库的使用介绍
Aug 08 #PHP
浅析php与数据库代码开发规范
Aug 08 #PHP
九个你必须知道而且又很好用的php函数和特点
Aug 08 #PHP
怎样使用php与jquery设置和读取cookies
Aug 08 #PHP
如何取得中文字符串中出现次数最多的子串
Aug 08 #PHP
php读取图片内容并输出到浏览器的实现代码
Aug 08 #PHP
You might like
php读取图片内容并输出到浏览器的实现代码
2013/08/08 PHP
更改localhost为其他名字的方法
2014/02/10 PHP
php中常见的sql攻击正则表达式汇总
2014/11/06 PHP
laravel框架select2多选插件初始化默认选中项操作示例
2020/02/18 PHP
javascript 面向对象继承
2009/11/26 Javascript
一个级联菜单代码学习及removeClass与addClass的应用
2013/01/24 Javascript
JavaScript判断变量是否为undefined的两种写法区别
2013/12/04 Javascript
showModalDialog模态对话框的使用详解以及浏览器兼容
2014/01/11 Javascript
对比分析AngularJS中的$http.post与jQuery.post的区别
2015/02/27 Javascript
javascript实现的固定位置悬浮窗口实例
2015/04/30 Javascript
javascript实现html页面之间参数传递的四种方法实例分析
2015/12/15 Javascript
Nodejs进阶:如何将图片转成datauri嵌入到网页中去实例
2016/11/21 NodeJs
jQuery UI制作选项卡(tabs)
2016/12/13 Javascript
Vue自定义指令封装节流函数的方法示例
2018/07/09 Javascript
用vuex写了一个购物车H5页面的示例代码
2018/12/04 Javascript
js实现随机8位验证码
2020/07/24 Javascript
[49:42]DOTA2上海特级锦标赛主赛事日 - 3 胜者组第二轮#2Secret VS EG第一局
2016/03/04 DOTA
[01:03:03]VP vs Mineski 2018国际邀请赛淘汰赛BO3 第一场 8.22
2018/08/23 DOTA
[03:12]完美世界DOTA2联赛PWL DAY7集锦
2020/11/06 DOTA
剖析Python的Twisted框架的核心特性
2016/05/25 Python
浅谈python字典多键值及重复键值的使用
2016/11/04 Python
Django项目中model的数据处理以及页面交互方法
2018/05/30 Python
使用Python快速搭建HTTP服务和文件共享服务的实例讲解
2018/06/04 Python
Python在图片中插入大量文字并且自动换行
2019/01/02 Python
Python-Tkinter Text输入内容在界面显示的实例
2019/07/12 Python
利用python3筛选excel中特定的行(行值满足某个条件/行值属于某个集合)
2020/09/04 Python
Python在centos7.6上安装python3.9的详细教程(默认python版本为2.7.5)
2020/10/15 Python
windows+vscode安装paddleOCR运行环境的步骤
2020/11/11 Python
python爬虫中采集中遇到的问题整理
2020/11/27 Python
CSS3教程:新增加的结构伪类
2009/04/02 HTML / CSS
CSS3的column-fill属性对齐列内容高度的用法详解
2016/07/01 HTML / CSS
英国男士时尚购物网站:Stuarts London
2017/10/22 全球购物
业务主管岗位职责范本
2013/12/25 职场文书
办护照工作证明
2014/10/01 职场文书
2014年减负工作总结
2014/12/10 职场文书
2014小学年度工作总结
2014/12/20 职场文书