PHP手册之变量范围

前言

PHP手册系列文章,会挑选一些手册中有意思的评论进行翻译
手册目录: 语言参考---变量---变量范围
参考详情

评论部分

  1. 提出一些有趣的代码,在类函数里面使用static变量
class sample_class
{
  public function func_having_static_var($x= NULL)
  {
    static $var = 0;
    if ($x === NULL)
    { return $var; }
    $var = $x;
  }
}

$a = new sample_class();
$b = new sample_class();
echo $a->func_having_static_var()."\n";
echo $b->func_having_static_var()."\n";
// this will output (as expected):
//  0
//  0

$a->func_having_static_var(3);
echo $a->func_having_static_var()."\n";
echo $b->func_having_static_var()."\n";
// this will output:
//  3
//  3
// maybe you expected:
//  3
//  0
?>

本来我们期望的可能是输出3.0,因为两个不同的实例,调用public非static方法,应该是互不干扰的.但是结果却告诉我们并非如此,方法中的static变量会作用于整个类方法以及所有的类实例,而不仅仅针对某一个实例.如果你想得到期望的输出,你应该放弃使用方法内static变量,像这样:

<?php
class sample_class
{ protected $var = 0;    //使用类内非static变量代替
  function func($x= NULL)
  { $this->var= $x; }
}

?>

我觉得正常的行为情况下是不应该像第一个例子那样使用static变量的,但也许你会用到(比如我),希望这些能对你有用.

你也可以通过类的实例来动态添加该实例的内部public变量,就像$instance->$param = $value 就会向$instance中动态添加了public的$param,当然这只针对该实例有效,不会对类及其他实例造成影响,其实该用法没什么卵用,只是想说php真特么强,笑尿.
  1. 与java和C++不同的是,在循环块和if块语句中的变量可以在块外访问
for($j=0;$j<3;$j++)
{
     if($j == 1)
        $a = 4;
}
echo $a;  //输出 4
  1. 对于嵌套函数,global始终指明的是最外层的全局变量,而不是指上一层的范围,如下将不会按照预期输出
// $var1 is not declared in the global scope
function a($var1){
    function b(){
        global $var1;
        echo $var1; // there is no var1 in the global scope so nothing to echo
    }
    b();
}
a('hello');

如果此时在function a()之上添加$var1 = 'some',将会得到输出结果'some'

  1. 提出一段有趣的代码
class obj{
    public function __destruct(){
        echo 'destruct';
    }
}

function foo ()
{
   global $testvar;
   $localvar = new obj();
   $testvar = &$localvar;
}

foo ();
var_dump($testvar);   
/*
输出
destruct
NULL
*/

因为对象引用是引用同一标识符,所以当方法结束后,相当于unset($localvar),此时没有其他标识符指向类实例,所以调用destruct,同时$testvar也相当于被unset了,为NULL,如果修改function如下

function foo ()
{
   global $testvar;
   $localvar = new obj();
   $testvar = $localvar;  //此处将引用改为赋值
}
/*
输出
object(obj)#1 (0) {
}
destruct
*/

可以看到,赋值跟引用是有区别的,赋值会copy一份标识符指向类实例,所以对$localvar的释放并不会影响$testvar,所以直到程序的最后才会调用destruct.具体的赋值与引用的区别可以参见:http://segmentfault.com/a/1190000002928594

  1. 关于类内方法使用static变量,1中已经提到过,这里指出,static变量不支持继承
class A {
    function Z() {
        static $count =0;        
        printf("%s: %d\n",get_class($this), ++$count);
    }
}

class B extends A {}
$a = new A();
$b = new B();
$a->Z();
$a->Z();
$b->Z();
$a->Z();
?>

/*
输出:
A: 1
A: 2
B: 1
A: 3
*/

可以看到,类A和类B使用了不同的静态变量,即使他们使用的是同一个方法,而且就算function Z是静态方法,结果也是一样

  1. 这里提一下,使用超全局$GLOBALS数组比使用global关键字更快
  2. 如果static变量是一个数组,并且返回它的某个元素的时候,会返回该元素的引用
function incr(&$int) {
  return $int++;
}

function return_copyof_scalar() {
  static $v;
  if (!$v)   
    $v = 1;
  return($v); 
} 

function return_copyof_arrayelement() {
  static $v;
  if (!$v) {
    $v = array();
    $v[0] =1;
  }
  return($v[0]);
} 

echo "scalar: ".
     incr(return_copyof_scalar()).
     incr(return_copyof_scalar()).
     "\n"; 
echo "arrayelement: ".
     incr(return_copyof_arrayelement()).
     incr(return_copyof_arrayelement()).
     "\n"; 
?>
/*
期望输出:
scalar:11
array element:11
*/

/*
他测试的结果:
scalar:11
array emelent:12
*/

但是我在php7上进行了同样的测试,发现结果跟期望输出是一样的,所以这里的数组元素并没有按照引用返回,如果我们要使用引用,应该像&function这样声明函数,也需要像&function一样调用它,但是有一个例外,就是当incr(return_copyof_scalar())这样作为参数调用的时候,不需要添加&,如此只需要在上述例子中的函数声明部分各自添加&就可以得到输出结果

scalar:12
array element:12
  1. 有时候你需在在其他多个函数中访问同一个static,同时这个static的可见范围也是非全局的,这里有一个简单的方法解决这个问题
  // We need a way to get a reference of our static
  function &getStatic() {
    static $staticVar;
    return $staticVar;
  }
  // Now we can access the static in any method by using it's reference
  function fooCount() {
    $ref2static = & getStatic();
    echo $ref2static++;
  }
  fooCount(); // 0
  fooCount(); // 1
  fooCount(); // 2
  1. 你不可以将方法里的static与传入进来的引用参数关联,但是你可以使用数组的形式对引用进行保存与操作
function test($arr = null){
    static $my;
    if(!$arr)return $my[0];
    $my = $arr;
}

$you = 'hello';
test(array(&$you));
$you = 'world';
var_dump(test());
/*
输出
world
*/
  1. 即使某个被include进来的file使用return返回一个value,该value仍然与include的文件中的同名value保持同一个访问范围
$foo = 'aaa';
$bar = include('include.php');
echo($foo.' / '.$bar);

// include.php
$foo = 'bbb';
return $foo;

/*
期望输出:aaa/bbb
实际输出:bbb/bbb
*/
  1. 一个比较有意思的代码
  class A
   {
     function __destruct()
      {
        global $g_Obj;
        echo "<br>#step 2: ";
        var_dump($g_Obj);
      }

     function start()
      {
        global $g_Obj;
        echo "<br>#step 1: ";
        var_dump($g_Obj);
      }
   };

  $g_Obj = new A();       // start here
  $g_Obj->start();

?>

/*
输出
#step 1: object(A)#1 (0) { }
#step 2: object(A)#1 (0) { }
*/

由上可以知道,__destruct()是在执行完后才销毁类实例的

  1. 嵌套函数需要注意重复声明以及第一次声明
function norm($a, $b) {
    static $first_time = true;
    if($first_time) {
        function square($x)
        {
            return $x * $x;
        }
        $first_time = false;
    }
    return sqrt(square($a) + square($b));
}

print norm(5,4);
print square(4);
print norm(6,5);

该例子能正确输出结果,如果将function中的判断语句if($first_time)去掉,就会造成重复声明的错误,同时,如果在语句print norm(5,4)之前调用square函数,将会导致undefined function 的错误,必须先执行一遍外部的norm函数,告诉外部存在其内部定义声明的square函数

  1. 当定义一个static变量时,你可以这样声明
static $var =1; //numbers
static $var ='strings';
static $var = array(1,'a',3);//array construct

但是你不可以这样声明(error)

static $var = some_function('arg');
static $var = (some_function('arg'));
static $var = 2+3;//any expression
static $var = new object;
  1. 当使用远程调用php文件时,远程文件中的变量将不能在调用文件中使用
// remotefile.php
$paramVal=10;

// localfile.php
include "[http://example.com/remotefile.php](http://example.com/remotefile.php)";
echo "remote-value= $paramVal";   //将不会得到预期值

这里提一下,本人没有亲自测试过,需要你自己转转脑瓜动动手测试下真伪

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容