• CI框架源码学习笔记5——Hooks.php


    接着Benchmark.php往下看,下一个引入的文件是Hooks.php,我们称之为钩子。它的目的是在不改变核心文件的基础上,来修改框架的内部运作流程。具体使用方法参见手册http://codeigniter.org.cn/user_guide/general/hooks.html。

    首先看类里面的几个属性,

    public $enabled = FALSE;  用来表示钩子是否可用

    public $hooks = array();  配置文件中的信息

    protected $_objects = array();  缓存用变量,用来储存挂钩点方法对应的对象

    protected $_in_progress = FALSE;  表示当前钩子是否正在进程中

      public function __construct()
        {
            $CFG =& load_class('Config', 'core');
            log_message('info', 'Hooks Class Initialized');
    
            // If hooks are not enabled in the config file
            // there is nothing else to do
            if ($CFG->item('enable_hooks') === FALSE)
            {
                return;
            }
    
            // Grab the "hooks" definition file.
            if (file_exists(APPPATH.'config/hooks.php'))
            {
                include(APPPATH.'config/hooks.php');
            }
    
            if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
            {
                include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
            }
    
            // If there are no hooks, we're done.
            if ( ! isset($hook) OR ! is_array($hook))
            {
                return;
            }
    
            $this->hooks =& $hook;
            $this->enabled = TRUE;
        }

    再来看看构造函数,$CFG =& load_class('Config', 'core');是加载config组件,用来获取hook的配置(具体的配置获取流程,我们后面分析Config.php时再详细讨论),log_message('info', 'Hooks Class Initialized');用来在日志中记录调用钩子的信息,注意需要在config.php配置中修改log_threshold的对应等级才能记录,具体原理参见前面的Log.php章节。

    再往下判断配置中的enable_hooks属性是否设置为了true,如果没有设置那么不继续往下,所以$this->enabled属性不会置为true,实际上意味着此时钩子功能不生效。

    接着引入hooks.php文件,并且获取其中配置,若配置信息格式正确,设置$this->enabled = TRUE;

    此时构造函数结束,我们接着看call_hook方法。

      public function call_hook($which = '')
        {
            if ( ! $this->enabled OR ! isset($this->hooks[$which]))
            {
                return FALSE;
            }
    
            if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))
            {
                foreach ($this->hooks[$which] as $val)
                {
                    $this->_run_hook($val);
                }
            }
            else
            {
                $this->_run_hook($this->hooks[$which]);
            }
    
            return TRUE;
        }

    钩子功能的之所以生效是因为我们在Codeigniter.php文件中,多次使用了call_hook方法,该方法的参数我们称之为挂钩点,ci中的挂钩点有7个,具体参见文档,不细述。

    先判断enabled属性是否为true,并且相应挂钩点的配置内容是否存在,只有上面两个条件都符合才继续往下。 

    if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))这行代码的目的是判断是否多次调用该挂钩点,ci框架支持挂钩点多次调用,只需要在配置中写成数组形式即可。若是,循环对配置执行_run_hook,如不是就不需要循环。

      protected function _run_hook($data)
        {
            //判断$data是否时合法的可调用结构,如果是的话直接调用
            //主要是为了处理ambda 表达式/匿名函数(或闭包)作为钩子的这种情况
            if (is_callable($data))
            {
                is_array($data)
                    ? $data[0]->{$data[1]}()
                    : $data();
    
                return TRUE;
            }
            //如果不是数组,说明配置有问题
            elseif ( ! is_array($data))
            {
                return FALSE;
            }
    
            //规避当前钩子正在执行其他脚本的情况
            if ($this->_in_progress === TRUE)
            {
                return;
            }
    
            if ( ! isset($data['filepath'], $data['filename']))
            {
                return FALSE;
            }
    
            //文件名赋值
            $filepath = APPPATH.$data['filepath'].'/'.$data['filename'];
    
            if ( ! file_exists($filepath))
            {
                return FALSE;
            }
    
            //读取配置内容赋值变量
            $class        = empty($data['class']) ? FALSE : $data['class'];
            $function    = empty($data['function']) ? FALSE : $data['function'];
            $params        = isset($data['params']) ? $data['params'] : '';
    
            //判断要调用的方法是否存在
            if (empty($function))
            {
                return FALSE;
            }
    
            //即将调用挂钩点方法,设置_in_progress为true,阻止再次进入脚本执行
            $this->_in_progress = TRUE;
    
            //判读$class是否为false,目的是判断配置中是否写了class,若没有写,那么可能
            //挂钩点的文件不是class的形式,只有一个函数,直接调用即可
            if ($class !== FALSE)
            {
                //先判断挂钩点对应的对象是否存在
                if (isset($this->_objects[$class]))
                {
                    if (method_exists($this->_objects[$class], $function))
                    {
                        $this->_objects[$class]->$function($params);
                    }
                    else
                    {
                        return $this->_in_progress = FALSE;
                    }
                }
                else
                {    
                    //判断类是否定义,若没有定义引入类文件
                    class_exists($class, FALSE) OR require_once($filepath);
                    
                    //判断类和类中的方法是否存在,若为false,是否钩子的进程变量_in_progress,推出_run_hook方法
                    if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function))
                    {
                        return $this->_in_progress = FALSE;
                    }
    
                    //类实例化,并且储存到_objects变量中
                    $this->_objects[$class] = new $class();
                    //调用类中的方法
                    $this->_objects[$class]->$function($params);
                }
            }
            //直接调用文件中的函数
            else
            {    
                //检查类是否已经定义,若没有定义那么引入类文件
                function_exists($function) OR require_once($filepath);
    
                //判断即将调用的方法是否存在,若不存在释放hook进程变量,退出_run_hook方法
                if ( ! function_exists($function))
                {
                    return $this->_in_progress = FALSE;
                }
                //传入参数,调用方法
                $function($params);
            }
            //释放进程变量,返回true,_run_hook方法执行成功
            $this->_in_progress = FALSE;
            return TRUE;
        }

    (代码分析写在注释中了)。

    Hooks.php文件就是上面的这些内容了,分析完毕,下面贴出全部代码。

    class CI_Hooks {
    
        /**
         * Determines whether hooks are enabled
         *
         * @var    bool
         */
        public $enabled = FALSE;
    
        /**
         * List of all hooks set in config/hooks.php
         *
         * @var    array
         */
        public $hooks =    array();
    
        /**
         * Array with class objects to use hooks methods
         *
         * @var array
         */
        protected $_objects = array();
    
        /**
         * In progress flag
         *
         * Determines whether hook is in progress, used to prevent infinte loops
         *
         * @var    bool
         */
        protected $_in_progress = FALSE;
    
        /**
         * Class constructor
         *
         * @return    void
         */
        public function __construct()
        {
            $CFG =& load_class('Config', 'core');
            log_message('info', 'Hooks Class Initialized');
    
            // If hooks are not enabled in the config file
            // there is nothing else to do
            if ($CFG->item('enable_hooks') === FALSE)
            {
                return;
            }
    
            // Grab the "hooks" definition file.
            if (file_exists(APPPATH.'config/hooks.php'))
            {
                include(APPPATH.'config/hooks.php');
            }
    
            if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/hooks.php'))
            {
                include(APPPATH.'config/'.ENVIRONMENT.'/hooks.php');
            }
    
            // If there are no hooks, we're done.
            if ( ! isset($hook) OR ! is_array($hook))
            {
                return;
            }
    
            $this->hooks =& $hook;
            $this->enabled = TRUE;
        }
    
        // --------------------------------------------------------------------
    
        /**
         * Call Hook
         *
         * Calls a particular hook. Called by CodeIgniter.php.
         *
         * @uses    CI_Hooks::_run_hook()
         *
         * @param    string    $which    Hook name
         * @return    bool    TRUE on success or FALSE on failure
         */
        public function call_hook($which = '')
        {
            if ( ! $this->enabled OR ! isset($this->hooks[$which]))
            {
                return FALSE;
            }
    
            if (is_array($this->hooks[$which]) && ! isset($this->hooks[$which]['function']))
            {
                foreach ($this->hooks[$which] as $val)
                {
                    $this->_run_hook($val);
                }
            }
            else
            {
                $this->_run_hook($this->hooks[$which]);
            }
    
            return TRUE;
        }
    
        // --------------------------------------------------------------------
    
        /**
         * Run Hook
         *
         * Runs a particular hook
         *
         * @param    array    $data    Hook details
         * @return    bool    TRUE on success or FALSE on failure
         */
        protected function _run_hook($data)
        {
            // Closures/lambda functions and array($object, 'method') callables
            if (is_callable($data))
            {
                is_array($data)
                    ? $data[0]->{$data[1]}()
                    : $data();
    
                return TRUE;
            }
            elseif ( ! is_array($data))
            {
                return FALSE;
            }
    
            // -----------------------------------
            // Safety - Prevents run-away loops
            // -----------------------------------
    
            // If the script being called happens to have the same
            // hook call within it a loop can happen
            if ($this->_in_progress === TRUE)
            {
                return;
            }
    
            // -----------------------------------
            // Set file path
            // -----------------------------------
    
            if ( ! isset($data['filepath'], $data['filename']))
            {
                return FALSE;
            }
    
            $filepath = APPPATH.$data['filepath'].'/'.$data['filename'];
    
            if ( ! file_exists($filepath))
            {
                return FALSE;
            }
    
            // Determine and class and/or function names
            $class        = empty($data['class']) ? FALSE : $data['class'];
            $function    = empty($data['function']) ? FALSE : $data['function'];
            $params        = isset($data['params']) ? $data['params'] : '';
    
            if (empty($function))
            {
                return FALSE;
            }
    
            // Set the _in_progress flag
            $this->_in_progress = TRUE;
    
            // Call the requested class and/or function
            if ($class !== FALSE)
            {
                // The object is stored?
                if (isset($this->_objects[$class]))
                {
                    if (method_exists($this->_objects[$class], $function))
                    {
                        $this->_objects[$class]->$function($params);
                    }
                    else
                    {
                        return $this->_in_progress = FALSE;
                    }
                }
                else
                {
                    class_exists($class, FALSE) OR require_once($filepath);
    
                    if ( ! class_exists($class, FALSE) OR ! method_exists($class, $function))
                    {
                        return $this->_in_progress = FALSE;
                    }
    
                    // Store the object and execute the method
                    $this->_objects[$class] = new $class();
                    $this->_objects[$class]->$function($params);
                }
            }
            else
            {
                function_exists($function) OR require_once($filepath);
    
                if ( ! function_exists($function))
                {
                    return $this->_in_progress = FALSE;
                }
    
                $function($params);
            }
    
            $this->_in_progress = FALSE;
            return TRUE;
        }
    
    }
  • 相关阅读:
    论人力资源的危机及其对策(3)
    maven常见问题问答
    bigtall的敏捷日记(1)
    项目管理沙龙的第一次聚会纪要
    论人力资源的危机与对策(2)
    Crest的OO核心实现
    阿里巴巴图标库,助力微信小程序开发
    微信小程序漂亮的搜索框【样式】
    C# windows 服务看门狗
    微信小程序生命周期
  • 原文地址:https://www.cnblogs.com/isuifeng/p/6607870.html
Copyright © 2020-2023  润新知