嵌套函数定义、与世隔绝的作用域

 

在一个php文档里,function的定义可以出现在call之前,也可以出现在其后。然而inner function(function 里面定义的function)并不是这样的,必须“先定义,再调用”。

<?php

f1();

function f1()

{

f2();

function f2()

{

echo ‘f2’;

}

}

?>

——————–

Fatal error: Call to undefined function: f2() in xxx.php on line 5

这样才行

<?php

f1();

function f1()

{

function f2()

{

echo ‘f2’;

}

f2();

}

?>

——————–

f2

ActionScript里面就不用这样,ActionScript里面,function可以访问到其定义所在的scope里面的变量,然而php不行,这是因为php里变量scope的设计有自己的特点。

php中变量的scope通常只有一个,那就是其定义所在的那个scope,这个scope可以扩展到被include的文件中去。但一个function有专属于自己scope,在function内部不能直接访问外部定义的变量。要想在自定义function中访问外部变量,可以使用global关键字,但有一点要注意:

global $a;

实际上是在所在functionlocal scope里创建了一个新变量$a,并将其“指向”global scope中的$a。所以对$a的改动会影响到$_GLOBAL[‘a’];,但是unset($a)却只能将在local scope$a销毁,而不影响到$_GLOBAL[‘a’]。这一点需要你在编程时多加注意!请参见:

http://spaces.msn.com/members/anotherblog/Blog/cns!1p2pEqx-MdcDtr92-Z1Jl5HA!219.entry

 

 

为什么inner function必须“先定义,再调用”,而文档域定义的function就不在乎调用和定义的出现顺序?有过C语言编程经验的朋友肯定对函数的声明(declaration)印象深刻,对一些C编译器,调用(call)若出现在定义(definition)之前(严格地说,还应满足返回值类型不是默认的int型),则必须事先声明。(不用C/C++好多年?看一下这个就想起来了)

void sayHello(char*); //要求加上这个declaration

  main() {

  sayHello("Arrix");

}

void sayHello(char *name) {

  printf("hello, %s!", name);

}

对于php这种弱类型语言,declaration当然没有必要了,php解析器首先处理global scope的所有function定义,方便了我们的编程。但有时候,php解释器不能预先处理一些function的定义,例如inner function的定义和条件function定义。在这些情况下,就要求我们“先定义,再调用”。

inner function demostration

<?php

f2(); //失败,因为f2还未被定义

function f1()

{

function f2()

{

echo ‘f2’;

}

}

?>

————————–

Fatal error: Call to undefined function: f2() in xxx.php on line 2

 

<?php

f1();

f2();//成功,执行f1后,f2被定义了

function f1()

{

function f2()

{

echo ‘f2’;

}

}

?>

————————–

f2

还有一点值得注意,php里所有的classfunction的作用于都是global scope,不管他们是在哪定义的。

phpscope的特性和global关键字的行为有其优点,但有时候会让编程不太方便。

<?php

function f1()

{

$message = "hi!"; //这个变量在f2里不可能直接访问到,这个scope是与世隔绝的

function f2()

{

echo ‘我永远访问不到f1里定义的$message,用global也不行,除非它被当成参数传进来 ‘;

}

}

可能你会觉得这种需要很少,其实在ActionScript里就经常会使用:

System.useCodepage = true;

load_btn.onRelease = function() {

var textLoader = new LoadVars();

var txtField = this._parent.text_txt;

//The raw data is passed to the function.

textLoader.onData = function(rawData) {

txtField.text = replace(rawData);

};

//replace every "\r\n" with newline to avoid displaying a blank line

function replace(rawData) {

return rawData.split("\r\n").join(newline);

}

textLoader.load(file_txt.text);

};

 

?>

php的这种特性利弊皆有,首先是简单,classfunction都是global scope里的,这使得以任何形式include的文件中定义的functionclass全局可用。scope可向包含文件扩展,而不像c语言有文件作用域,这样大大简化了编程。简单的模型也能带来速度优势。代价是牺牲灵活性,并可能造成运行时才能出现的call to undefined functionredefine function错误,使程序的robustness大打折扣。因此应该尽量避免使用条件functioninner function。取而代之,我们应该使用更直观更安全的设计方式。

 

要特别注意一点,如果你的include是放在一个函数体中,那么很可能会出现不知不觉的inner function定义。

例如:

test.php

———————-

<?php

function includeFile()

{

include(‘inner.php’);

}

includeFile();

?>

———————

inner.php

———————

<?php

$msg = ‘hello’;

showMsg();

function showMsg()

{

global $msg;

var_dump($msg);

}

?>

单独看inner.php,怎么都不可能有错。然而包含到test.php中时,却始终输出NULL。这是因为inner.php是在一个function中被include的,其scope扩展自function includeFile,所以$msg实际上成了一个includeFile函数的local variable。在这种情况下,showMsg无论如何都访问不到inner.php中定义的$msg,除非当成参数传进来。我给SmartTemplate添加INCLUDE_PHP功能时(把模版中的

<!– INCLUDE_PHP –>

{FILENAME}

<!– END_INCLUDE_PHP –>

替换成

<?php

include(‘FILENAME’);

?>),就遇到了这个问题,非常恼火,因为这句include总是在SmartTemplate::outputscope中执行。

你可能会想到这种写法:

inner.php

———————

<?php

global $msg;

$msg = ‘hello’;

showMsg();

function showMsg()

{

global $msg;

echo $msg;

}

?>

不错,这样的确可以,但是极不安全,因为很可能会以外地破坏掉$_GLOBAL[‘msg’]的值。怎么办?加一个guard

inner.php

———————

<?php

global $msg;

if (isset($msg)) {

trigger_error(‘variable conflict’);

die();

}

$msg = ‘hello’;

showMsg();

function showMsg()

{

global $msg;

echo $msg;

}

?>

天哪!多么糟糕的代码!不知道Smarty的文件包含有没有这个问题?

Advertisements
This entry was posted in PHP Rocks. Bookmark the permalink.

4 Responses to 嵌套函数定义、与世隔绝的作用域

  1. Unknown says:

    wow gold!All wow gold US Server 24.99$/1000G on sell! Cheap wow gold,wow gold,wow gold,Buy Cheapest/Safe/Fast WoW US EU wow gold Power leveling wow gold from the time you wWorld of Warcraft gold ordered!

    wow power leveling wow power leveling power leveling wow power leveling wow powerleveling wow power levelingcheap wow power leveling wow power leveling buy wow power leveling wow power leveling buy power leveling wow power leveling cheap power leveling wow power leveling wow power leveling wow power leveling wow powerleveling wow power leveling power leveling wow power leveling wow powerleveling wow power leveling buy rolex cheap rolex wow gold wow gold wow gold wow gold -29650352076303

  2. Unknown says:

    wow gold!All wow gold US Server 24.99$/1000G on sell! Cheap wow gold,wow gold,wow gold,Buy Cheapest/Safe/Fast WoW US EU wow gold Power leveling wow gold from the time you wWorld of Warcraft gold ordered!

    wow power leveling wow power leveling power leveling wow power leveling wow powerleveling wow power levelingcheap wow power leveling wow power leveling buy wow power leveling wow power leveling buy power leveling wow power leveling cheap power leveling wow power leveling wow power leveling wow power leveling wow powerleveling wow power leveling power leveling wow power leveling wow powerleveling wow power leveling buy rolex cheap rolex wow gold wow gold wow gold wow gold -34618404329030

  3. Unknown says:

    wow gold!All wow gold US Server 24.99$/1000G on sell! Cheap wow gold,wow gold,wow gold,Buy Cheapest/Safe/Fast WoW US EU wow gold Power leveling wow gold from the time you wWorld of Warcraft gold ordered!

    wow power leveling wow power leveling power leveling wow power leveling wow powerleveling wow power levelingcheap wow power leveling wow power leveling buy wow power leveling wow power leveling buy power leveling wow power leveling cheap power leveling wow power leveling wow power leveling wow power leveling wow powerleveling wow power leveling power leveling wow power leveling wow powerleveling wow power leveling buy rolex cheap rolex wow gold wow gold wow gold wow gold -209940069297930

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s