戏说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小时内删除。
发表评论