戏说PHP的嵌套函数
PHP很早就⽀持嵌套函数了。并是不PHP5.3有闭包时才有的。然⽽,它却不是象JS,AS那样的闭包嵌套。即它的嵌套函数根本⽆闭包模式的逃脱。
PHP嵌套函数有⼀些特别之处。最特别的是,当外部函数被调⽤时,内部函数就会⾃动进⼊全局域中,成为新的定义函数。
所以,当外部函数确保是被调⽤⼀次,不会被调⽤⼆次,那么,可以写嵌套函数在其中。否则,就会引发致命错误。
但若我们仍想在⼀个可被调⽤多次的函数中定义⼀个内部函数,那么,该如何处理?
我们象在全局定义函数⼀样使⽤:
if (!function_exists('你的函数名')){
}
因此,全局函数的使⽤,常常⽤于⼀些特别的⽬的。同时要清楚,这样的函数,实际就是定义的全局函数。因此,它没有类对它封装,更没有命名空间。
看⼀下PHP⼿册中是如何说的:
Php代码
1. <?php
2. function foo()
3. {
4.  function bar()
5.  {
6.    echo "I don't exist until foo() is called.\n";
7.  }
8. }
9.
10. /* 现在还不能调⽤bar()函数,因为它还不存在 */
11.
12. foo();
13.
14. /* 现在可以调⽤bar()函数了,因为foo()函数
15. 的执⾏使得bar()函数变为已定义的函数 */
16.
17. bar();
18.
19. ?>
<?php
function foo()
{
function bar()
{
echo "I don't exist until foo() is called.\n";
}
}
/* 现在还不能调⽤bar()函数,因为它还不存在 */
foo();
/* 现在可以调⽤bar()函数了,因为foo()函数
的执⾏使得bar()函数变为已定义的函数 */
bar();
>
PHP 中的所有函数和类都具有全局作⽤域,可以在内部定义外部调⽤,反之亦然。
我们不妨先看⼀下函数:
Php代码
1. function outer( $msg ) {
2.    function inner( $msg ) {
3.        echo 'inner: '.$msg.' ';
4.    }
5.    echo 'outer: '.$msg.' ';
6.    inner( $msg );
7. }
8.
9. inner( 'test1' );  // Fatal error:  Call to undefined function inner()
10. //上⾯出错,是因为外部函数还没有调⽤,所以出错。
11. outer( 'test2' );  // outer: test2 inner: test2
12. inner( 'test3' );  // inner: test3
13. outer( 'test4' );  // Fatal error:  Cannot redeclare inner()
14. //上⾯出错,是因为,外部函数被调⽤时,内部函数被重定义了。
function outer( $msg ) {
function inner( $msg ) {
echo 'inner: '.$msg.' ';
}
echo 'outer: '.$msg.' ';
inner( $msg );
}
inner( 'test1' );  // Fatal error:  Call to undefined function inner()
//上⾯出错,是因为外部函数还没有调⽤,所以出错。
outer( 'test2' );  // outer: test2 inner: test2
inner( 'test3' );  // inner: test3
outer( 'test4' );  // Fatal error:  Cannot redeclare inner()
//上⾯出错,是因为,外部函数被调⽤时,内部函数被重定义了。
这⾥,我们再看⼀下,⼀个⾃动加载类,其中的做法
Php代码
1. static public function initAutoload(){
2.        //初始化Autoload Callable List
3.        self::setAutoloadCallableList();
4.        //初始化 $classList
5.        self::$classList = uxAutoloadConfig::getClassList();
6.
7.        //如果有spl_autoload_register,则直接设置
8.        if (function_exists('spl_autoload_register')){
9.            ini_set('unserialize_callback_func', 'spl_autoload_call');
10.            spl_autoload_register(array('uxAutoload', 'splSimpleAutoload'));
11.        }elseif (!function_exists('__autoload')){  //否则要使⽤__autoload函数。
12.            ini_set('unserialize_callback_func', '__autoload');
13.
14.            //因为没有spl_autoload, 所以, 这⾥要定义⼀个__autoload函数.
15.            function __autoload($class){
16.                if( self::splSimpleAutoload($class)== true)
17.                    return true;
18.                //因为没有spl_autoload_register,所以在类未加载成功时,要处理Callable List
19.                foreach(self::$autoloadCallables as $key => $callable ){
20.                    if (class_exists($class, false)){
21.                        $classObj=self::$autoloadObjectList[$callable[0]];
22.                    }else{
23.                        $className=$callable[0];
24.                        $classObj = new $className();
25.                        self::$autoloadObjectList[$class] = &$classObj;
26.                    }
27.                    if (method_exists($classObj,$callable[1])){
28.                        $method=$callable[1];
29.                        if ($classObj->$method($class)==true)
30.                            return true;
31.                    }else{
32.                        trigger_error('Autoload method '.$method.' not found in class '.$className.'!', E_USER_ERROR);
33.                        return false;
34.                    }
35.                }
36.            }
37.        }
38.    }
static public function initAutoload(){
//初始化Autoload Callable List
self::setAutoloadCallableList();
//初始化 $classList
self::$classList = uxAutoloadConfig::getClassList();
/
/如果有spl_autoload_register,则直接设置
if (function_exists('spl_autoload_register')){
ini_set('unserialize_callback_func', 'spl_autoload_call');
spl_autoload_register(array('uxAutoload', 'splSimpleAutoload'));
}elseif (!function_exists('__autoload')){  //否则要使⽤__autoload函数。
ini_set('unserialize_callback_func', '__autoload');
//因为没有spl_autoload, 所以, 这⾥要定义⼀个__autoload函数.
function __autoload($class){
if( self::splSimpleAutoload($class)== true)
return true;
//因为没有spl_autoload_register,所以在类未加载成功时,要处理Callable List
foreach(self::$autoloadCallables as $key => $callable ){
if (class_exists($class, false)){
$classObj=self::$autoloadObjectList[$callable[0]];
}else{
$className=$callable[0];
$classObj = new $className();
self::$autoloadObjectList[$class] = &$classObj;
}
if (method_exists($classObj,$callable[1])){
$method=$callable[1];
if ($classObj->$method($class)==true)
return true;
}else{
trigger_error('Autoload method '.$method.' not found in class '.$className.'!', E_USER_ERROR);
return false;
}
}
}
}
}
很明显,它是定义了⼀个内部函数function __autoload($class),以防没有'spl_autoload_register'。⽽这个类的这个函数,任⼀request请求中,只运⾏⼀次。
但是,如果要运⾏多次,⽐如,以下函数中,定义了⼀个全局的TRACE函数。这个函数的⽬的是在⽤户使⽤标准MVC⽅式时,才提供此TRACE函数给⽤户使⽤。引导⽤户⾛正确的⽅向。实际上,也可以看出,如果⽤户⽤不到此类,很可能,TRACE函数就不是这么⼏⾏代码。由此,这⼀做法确实精简了相当多的代码。
Php代码
1.  static public function getInstance($config = 0 ,$className=NULL){
2.      if (!function_exists('trace')){ //specially for ajax debug!!
3.          function trace($var){
4.              $string=print_r($var,true);
5.              require_once(UXERHDIR.'../uxLogger/uxLogger.class.php');
6.              uxLogger::getInstance()->logg('INFO',
7.              '/*************************** BEGIN INFO BY TRACE: ***************************\r\n '
8.              .$string
9.              .'/***************************  END INFO BY TRACE  ***************************\r\n' );
10.          }
11.      }
php延时函数
12.      if (!isset(self::$instance)){
13.          if (is_array($config)){
14.              $options=$config;
15.          }else{
16.                  if ($config == NULL)
17. $config = 0;
18.              $options=uxErrorHandlerConfig::get($config);
19.          }
20.          $class=($className==NULL)?'uxErrorHandler':$className;
21.          self::$instance = new $class($options);
22.      }
23.      return self::$instance;
24.  }
static public function getInstance($config = 0 ,$className=NULL){
if (!function_exists('trace')){ //specially for ajax debug!!
function trace($var){
$string=print_r($var,true);
require_once(UXERHDIR.'../uxLogger/uxLogger.class.php');
uxLogger::getInstance()->logg('INFO',
'/*************************** BEGIN INFO BY TRACE: ***************************\r\n '
.$string
.'/***************************  END INFO BY TRACE  ***************************\r\n' );
}
}
if (!isset(self::$instance)){
if (is_array($config)){
$options=$config;
}else{
if ($config == NULL)
$config = 0;
$options=uxErrorHandlerConfig::get($config);
}
$class=($className==NULL)?'uxErrorHandler':$className;
self::$instance = new $class($options);
}
return self::$instance;
}
可以看出,嵌套函数,是⼀种有条件全局函数,你可以控制,在什么情况下提供这样的全局函数给⽤户使⽤。但也需要注意,过多的全局函数则会产⽣“全局污染”,所以,不可多⽤。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。