热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

php嵌套函数闭包嵌套函数+递归调用的Fatal错误分析

嵌套函数定义时先判断function_exists防止递归调用外部函数导致两次定义内部函数导致致命错误看一下PHP手册中是如何说的:





嵌套函数 定义时 先判断function_exists 防止 递归调用外部函数 导致两次定义内部 函数 导致致命错误





看一下PHP手册中是如何说的:






function foo()

{

  function bar()

  {

    echo "I don't exist until foo() is called.\n";

  }

}





/* 现在还不能调用bar()函数,因为它还不存在 */





foo();





/* 现在可以调用bar()函数了,因为foo()函数

   的执行使得bar()函数变为已定义的函数 */







?> 

?





PHP 中的所有函数和类都具有全局作用域,可以在内部定义外部调用,反之亦然。





?





我们不妨先看一下函数:





function outer( $msg ) { 

    function inner( $msg ) { 

        echo 'inner: '.$msg.' '; 

    } 

    echo 'outer: '.$msg.' '; 

    inner( $msg ); 




意这里 内部是创建了一个 有名字的函数 并放到全局函数表中,
如果是 inner 定义变成 function(){} 匿名函数定义 则会类似create_function 创建一个 \0lamab_function1 (后面1自动递增) 的函数到全局函数表


如果加上 $obj = function inner() {};或者 $obj=function () {}; 则 是使用该有名函数或者匿名函数 创建一个闭包对象 (use 变量变成这个闭包类的静态对象)

注意如果是 $obj=function ..则一定 要加分号结束
function f()
{
    $a = 1;
    $b = 3;
    $func =  function ($a)use($b) {
        echo json_encode($a).PHP_EOL;
        echo json_encode($b).PHP_EOL;
    };
    return $func;
}
$func = f();
$func(3);
以上示例输出3 3

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() 

//上面出错,是因为,外部函数被调用时,内部函数被重定义了。

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函数就不是这么几行代码。由此,这一做法确实精简了相当多的代码。





    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;

    }

?可以看出,嵌套函数,是一种有条件全局函数,你可以控制,在什么情况下提供这样的全局函数给用户使用。但也需要注意,过多的全局函数则会产生“全局污染”,所以,不可多用。






推荐阅读
author-avatar
很呆很傻很天真2010_545
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有