SugarCRM源码分析之钩子


        本篇主要分析下SugarCRM的钩子源码实现,即使一开始对SugarCRM不熟,掌握了钩子的用法,也能写写相关的功能。

// 调用钩子
LogicHook::initialize()->call_custom_logic('', 'entry_point_variables_setting');

// 返回实例
// ./include/utils/LogicHook.php
static function initialize() {
    if (empty($GLOBALS['logic_hook']))
        $GLOBALS['logic_hook'] = new LogicHook();
    return $GLOBALS['logic_hook'];
}

// 调用钩子,$module_dir为空调用application,不为空调用module
// 当$module_dir为空时,$this->bean为null
function call_custom_logic($module_dir, $event, $arguments = array()) {

    // $this->bean默认为null
    // 如果传过来的$module_dir为空,那么下面的BeanFactory::getBean($module_dir)返回的还是null
    /*
        // ./data/SugarBean.php
        public static function getBean($module, $id = null, $params = array(), $deleted = true)
        {
            ```
            $beanClass = self::getBeanName($module);
            if (empty($beanClass) || !class_exists($beanClass)) return null;
            ```
        }

        // 获取bean名称,如果为空,返回false
        // 否则返回已有的$bean_classes
        // 或是$beanList里的类【该数组在./include/modules.php里】
        public static function getBeanName($module)
        {
            if(!empty(self::$bean_classes[$module])) {
                return self::$bean_classes[$module];
            }
            global $beanList;
            if (empty($beanList[$module]))  {
                return false;
            }

            return $beanList[$module];
        }
    */
    $origBean = $this->bean;
    if ($origBean === null) {
        $bean = BeanFactory::getBean($module_dir);
        if ($bean instanceOf SugarBean) {
            $this->setBean($bean);
        }
    }

    // declare the hook array variable, it will be defined in the included file.
    // 写过钩子都是通过此数组定义
    $hook_array = null;

    // 看到上篇日志管理那篇博客应该知道下面debug方法不会执行
    // 除非在config.php把日志级别配置为debug
    if (isset($GLOBALS['log'])) {
        $GLOBALS['log']->debug("Hook called: $module_dir::$event");
    }

    // 如果$module_dir不为空,那么调用的是模块钩子
    // 这里的模块,就是页面导航栏中的具体菜单项
    if (!empty($module_dir)) {
        // This will load an array of the hooks to process
        $hooks = $this->getHooks($module_dir);
        if (!empty($hooks)) {
            $this->process_hooks($hooks, $event, $arguments);
        }
    }

    // 否则调用的是应用钩子
    // 获取全部的应用钩子
    $hooks = $this->getHooks('');
    if (!empty($hooks)) {

        // 执行钩子逻辑
        $this->process_hooks($hooks, $event, $arguments);
    }

    // 如果$module_dir为空,$origBean便为null
    // 那么就是把$this->bean置为null
    if ($origBean === null) {
        $this->setBean($origBean);
    }
}

// 获取钩子
public function getHooks($module_dir, $refresh = false) {

    if ($refresh || !isset(self::$hooks[$module_dir])) {
        self::$hooks[$module_dir] = $this->loadHooks($module_dir);
    }
    return self::$hooks[$module_dir];
}

// 在指定文件中加载钩子
public function loadHooks($module_dir) {
    $hook_array = array();
    if (!empty($module_dir)) {
        $custom = "custom/modules/$module_dir";
    } else {
        $custom = "custom/modules";
    }

    // 这一步可以看出当$module_dir为空时,查找的是application的钩子文件
    // 否则查找的是指定模块的钩子文件
    foreach (SugarAutoLoader::existing(
            "$custom/logic_hooks.php", SugarAutoLoader::loadExtension("logichooks", empty($module_dir) ? "application" : $module_dir)
    ) as $file) {
        if (isset($GLOBALS['log'])) {
            $GLOBALS['log']->debug('Including hook file: ' . $file);
        }
        include $file;
    }
    return $hook_array;
}

// ./include/utils/autoloader.php
// self::$extensions存储的是./ModuleInstall.php中的数据,在SugarAutoLoader::init()中加载执行
// 执行完此方法后,返回的是 custom/modules/application/Ext/LogicHoooks/logichooks.ext.php文件
public static function loadExtension($extname, $module = "application") {
    if (empty(self::$extensions[$extname]))
        return false;
    $ext = self::$extensions[$extname];
    if (empty($ext['file']) || empty($ext['extdir'])) {
        // custom rebuilds, can't handle
        return false;
    }
    if (isset($ext["module"])) {
        $module = $ext["module"];
    }
    if ($module == "application") {
        $file = "custom/application/Ext/{$ext["extdir"]}/{$ext["file"]}";
    } else {
        $file = "custom/modules/{$module}/Ext/{$ext["extdir"]}/{$ext["file"]}";
    }
    if (self::fileExists($file)) {
        return $file;
    }
    return false;
}

// 执行钩子逻辑
/*

Array
(
    [before_save] => Array
        (
            [0] => Array
                (
                    [0] => 1
                    [1] => BeforeSaveHooksMethod
                    [2] => custom/modules/ptsdb_double_points_rule/DoublePointsRuleHooks.php
                    [3] => DoublePointsRuleHooks
                    [4] => beforeSaveHooksMethod
                )

        )

    [after_save] => Array
        (
            [0] => Array
                (
                    [0] => 1
                    [1] => AfterSaveHooksMethod
                    [2] => custom/modules/ptsdb_double_points_rule/DoublePointsRuleHooks.php
                    [3] => DoublePointsRuleHooks
                    [4] => afterSaveHooksMethod
                )

        )

    [after_delete] => Array
        (
            [0] => Array
                (
                    [0] => 1
                    [1] => AfterDeleteHooksMethod
                    [2] => custom/modules/ptsdb_double_points_rule/DoublePointsRuleHooks.php
                    [3] => DoublePointsRuleHooks
                    [4] => afterDeleteHooksMethod
                )

        )

)
*/
function process_hooks($hook_array, $event, $arguments) {

    // 如果已经定义了相关的钩子,那么在这就可以开始执行了
    // 钩子是按照index从小到大的次序来执行的
    // Now iterate through the array for the appropriate hook
    if (!empty($hook_array[$event])) {

        // Apply sorting to the hooks using the sort index.
        // Hooks with matching sort indexes will be processed in no particular order.
        $sorted_indexes = array();
        foreach ($hook_array[$event] as $idx => $hook_details) {
            $order_idx = $hook_details[0];
            $sorted_indexes[$idx] = $order_idx;
        }
        asort($sorted_indexes);

        $process_order = array_keys($sorted_indexes);

        foreach ($process_order as $hook_index) {
            $hook_details = $hook_array[$event][$hook_index];

            // 如果钩子执行文件不存在,记日志报错【不会执行】,进入下一次循环
            if (!file_exists($hook_details[2])) {
                if (isset($GLOBALS['log'])) {
                    $GLOBALS['log']->error('Unable to load custom logic file: ' . $hook_details[2]);
                }
                continue;
            }

            // 加载钩子执行文件
            include_once($hook_details[2]);

            // 钩子执行类
            $hook_class = $hook_details[3];

            // 钩子执行方法
            $hook_function = $hook_details[4];

            // Make a static call to the function of the specified class
            // TODO Make a factory for these classes.  Cache instances accross uses
            // 如果类和方法一样,直接new钩子类,把钩子方法及参数传进去
            if ($hook_class == $hook_function) {
                if (isset($GLOBALS['log'])) {
                    $GLOBALS['log']->debug('Creating new instance of hook class ' . $hook_class . ' with parameters');
                }

                // $this->bean不为空,大都是数据库相关的操作 类
                if (!is_null($this->bean))
                    $class = new $hook_class($this->bean, $event, $arguments);

                    // 不是的话,就不用传
                else
                    $class = new $hook_class($event, $arguments);

                    // 类和方法不同名
            }else {
                if (isset($GLOBALS['log'])) {
                    $GLOBALS['log']->debug('Creating new instance of hook class ' . $hook_class . ' without parameters');
                }

                // 实例化钩子类
                $class = new $hook_class();

                // 执行钩子
                if (!is_null($this->bean)) {
                    $callback = array($class, $hook_function);
                    // & is here because of BR-1345 and old broken hooks
                    // that use &$bean in args.
                    // 这里主要是兼容以前版本的写法,基本上$params还是&$this->bean, $event, $arguments
                    $params = array_merge(array(&$this->bean, $event, $arguments), array_slice($hook_details, 5));
                    call_user_func_array($callback, $params);
                } else
                    $class->$hook_function($event, $arguments);
            }
        }
    }
}




你可能感兴趣的:(SugarCRM源码分析之钩子)