SugarCRM源码分析之SugarAutoLoader::init


        有了前面的三篇铺垫,现在可以整体过下SugarAutoLoader::init()方法了。

public static function init() {
    $config = SugarConfig::getInstance();

    /*
     * 在开发模式下,删除./cache/目录下的file_map.php和class_map.php
     * 每次在fileExists($filename)时,会直接判断文件是否存在
     * 而不是一开始读取file_map.php后放入$classMap中
     * When development mode is enabled, we bypass the usage
     * of the filemap and build the classmap dynamically on
     * every page load. We drop both cache file to make sure
     * when devMode is disabled again that the system is
     * properly initialized again withour the need for
     * running a QuickRepairRebuild.
     */
    self::$devMode = $config->get('developerMode', false);
    if (self::$devMode) {
        @unlink(sugar_cached(self::CACHE_FILE));
        @unlink(sugar_cached(self::CLASS_CACHE_FILE));
    }

    // Extensions included from config
    // 可以从配置文件里添加需要sugarcrm允许的后缀
    $exts = $config->get('autoloader.exts');
    if (is_array($exts)) {
        self::$exts += $exts;
    }

    // Excludes from config
    // 可以从配置文件里添加需要跳过的配置
    $exclude = $config->get('autoloader.exclude');
    if (is_array($exclude)) {
        self::$exclude += $exclude;
    }

    // Create file map
    // 创建./include/file_map、./include/class_map.php
    self::loadFileMap();

    // 加载composer引用的类【命名空间】
    // Composer integration
    self::loadComposerIncludePaths();
    self::loadComposerPsr4();
    self::loadComposerPsr0();

    // Build class map (implicitly includes Composer's classmap)
    // 看了几遍代码后,仍是没有发现隐式包含的composer类【self::loadFileMap()已经都包含】
    self::loadClassMap();

    // Register ourself (prepend)
    // 注册SugarCRM自己的autoload方法,这里true说明,每次自动加载把要加载类提到栈顶
    self::registerAutoload(true);

    // Load extensions
    // 加载模块扩展
    self::loadExts();
    /*
    protected static function loadExts() {
        include "ModuleInstall/extensions.php";
        self::$extensions = $extensions;
    }
    */
}


        下面就着重看看autoload的实现

public static function init() {
    ```
    // 注册SugarCRM自己的autoload方法
    registerAutoload(true);
    ```
}

public static function registerAutoload($prepend = true) {
    spl_autoload_register(array('SugarAutoLoader', 'autoload'), true, $prepend);
}

// 注意self::$classMapDirty变量,当./cache/class_map.php生成后,此变量为false
// 但是加载相关类后,会置为true,在项目结束时,调用sugar_cleanup中的
// SugarAutoLoader::saveClassMap()方法会再次生成./cache/class_map.php文件
// 这里有一条比较重要if (empty(self::$moduleMap)),这个判断会引用项目中二次开发的模块
public static function autoload($class) {
    // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
    $class = ltrim($class, '\\');

    $uclass = ucfirst($class);
    /*
      * 在此数组中的类不加载
    public static $noAutoLoad = array(
        'Tracker' => true,
        // this one is generated by ViewFactory for classic view, but if never actually exists
        'UsersViewClassic' => true,
    );
    */
    if (!empty(self::$noAutoLoad[$class])) {
        return false;
    }

    // try known classes
    // 加载class_map.php中的类
    if (isset(self::$classMap[$uclass])) {
        if (self::$classMap[$uclass]) {
            // No need for a file_exists, if it is in the map we have found it before
            // 这里不用再判断文件是否存在,因为这个已经在前面发现了
            require_once self::$classMap[$uclass];
            return true;
        }
        // 在class_map.php中,如果文件不存在,value会是false,因此会走到此步
        return false;
    }

    // try namespaces
    // 使用命名空间,这里会用到init里composer集成的数据
    // 优先加载psr-4,然后按照路径从多到少匹配
    /*
        public static function getFilenameForFQCN($class) {
            // PSR-4 has precedence
            if ($file = self::getFileNamePsr4($class)) {
                return $file;
            }

            return self::getFileNamePsr0($class);
        }

        #[
        #    'Sugarcrm\\Sugarcrm\\inc\\' => [
        #        '/custom/src',
        #        '/custom/',
        #     ]
        #]
        public static function getFileNamePsr4($class) {
            foreach (self::$namespaceMapPsr4 as $prefix => $paths) {
                foreach ($paths as $path) {
                    if (strpos($class, $prefix) === 0) {
                        $path = empty($path) ? '' : $path . '/';
                        $path .= str_replace('\\', '/', str_replace($prefix, '', $class)) . '.php';
                        if (self::fileExists($path)) {
                            return $path;
                        }
                    }
                }
            }
            return false;
        }

        public static function getFileNamePsr0($class) {
            foreach (self::$namespaceMap as $namespace => $paths) {
                foreach ($paths as $path) {
                    if (strpos($class, $namespace) === 0) {
                        $path = empty($path) ? '' : $path . '/';
                        if (false !== $pos = strrpos($class, '\\')) {
                            $path .= str_replace('\\', '/', substr($class, 0, $pos)) . '/';
                            $path .= str_replace('_', '/', substr($class, $pos + 1)) . '.php';
                        } else {
                            $path .= str_replace('_', '/', $class) . '.php';
                        }
                        if (self::fileExists($path)) {
                            return $path;
                        }
                    }
                }
            }
            return false;
        }

        public static function load($file) {
            if (self::fileExists($file)) {
                require_once $file;
                return true;
            }
            return false;
        }
    */
    if (false !== strpos($class, '\\')) {
        if ($file = self::getFilenameForFQCN($class)) {
            if (self::load($file)) {
                self::$classMap[$class] = $file;
                self::$classMapDirty = true;
                return true;
            }
        }
        self::$classMap[$class] = false;
        self::$classMapDirty = true;
        return false;
    }

    // 首次会加载include/modules.php文件
    // 此文件定义了页面tap显示顺序、应用的模块列表【$beanList】
    // 安全设置、管理员独有的权限(all)……
    if (empty(self::$moduleMap)) {
        if (isset($GLOBALS['beanFiles'])) {
            self::$moduleMap = $GLOBALS['beanFiles'];
        } else {
            include('include/modules.php');
            /*
                foreach(SugarAutoLoader::existing('include/modules_override.php', SugarAutoLoader::loadExtension("modules")) as $modExtFile) {
                    include $modExtFile;
                }

                // 这一步会加载self::loadExts()引入的模块数组self::$extensions
                public static function loadExtension($extname, $module = "application") {

                    // self::$extensions['modules'] = 
                    // array("section" => "beans", "extdir" => "Include", "file" => 'modules.ext.php', "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") {
                        // custom/application/Ext/Include/modules.ext.php
                        // 此文件是自动生成的,里面存放着二次开发的模块
                        // 如,ca_capital,投资交易,url为http://sugar/#ca_capital
                        $file = "custom/application/Ext/{$ext["extdir"]}/{$ext["file"]}";
                    } else {
                        // custom/modules/application/Ext/Include/modules.ext.php
                        $file = "custom/modules/{$module}/Ext/{$ext["extdir"]}/{$ext["file"]}";
                    }
                    if (self::fileExists($file)) {
                        return $file;
                    }
                    return false;
                }
            */
            self::$moduleMap = $beanFiles;
        }
    }

    // Try known modules
    // 引入上一步的模块映射
    if (!empty(self::$moduleMap[$class])) {
        require_once self::$moduleMap[$class];
        return true;
    }

    // HTMLPurifier为PHP富文本HTML过滤器了,采用了白名单机制
    // 有效杜绝了用户提交表单中的非法HTML标签,从而可以防止XSS攻击
    // ./vendor/HTMLPurifier
    if (strncmp('HTMLPurifier', $class, 12) == 0) {
        return HTMLPurifier_Bootstrap::autoload($class);
    }

    // Split on _, capitalize elements and make a path
    // foo_bar -> Foo/Bar.
    $class_file = join('/', array_map('ucfirst', explode('_', $class)));

    // Try known prefixes
    /*
        public static $prefixMap = array(
            'SugarACL' => "data/acl/",
            'SugarWidget' => "include/generic/SugarWidgets/",
            'Zend_' => 'vendor/',
            'SugarJob' => 'include/SugarQueue/jobs/',
            'MetaDataContext' => 'modules/ModuleBuilder/parsers/MetaDataContext/',
        );
    */
    foreach (self::$prefixMap as $prefix => $dir) {
        if (strncasecmp($prefix, $class, strlen($prefix)) === 0) {
            if ($file = self::requireWithCustom("{$dir}$class_file.php")) {
                self::$classMap[$uclass] = $file;
                self::$classMapDirty = true;
                return true;
            } else {
                break;
            }
        }
    }

    // 下面的几个判断是页面元素展现相关类

    // Special cases
    // Special case because lookup goes to $_REQUEST['module']
    /*
        protected static function getFilenameForViewClass($class) {
            $module = false;
            if (!empty($_REQUEST['module']) && substr($class, 0, strlen($_REQUEST['module'])) === $_REQUEST['module']) {
                //This is a module view
                $module = $_REQUEST['module'];
                $class = substr($class, strlen($module));
            }

            if (substr($class, 0, 4) == "View") {
                $view = strtolower(substr($class, 4));
                if ($module) {
                    return self::requireWithCustom("modules/$module/views/view.$view.php");
                } else {
                    return self::requireWithCustom("include/MVC/View/views/view.$view.php");
                }
            }
            return false;
        }
    */
    if ($file = self::getFilenameForViewClass($class)) {
        self::$classMap[$uclass] = $file;
        self::$classMapDirty = true;
        return true;
    }

    // Special case because widget name can be lowercased
    /*
        protected static function getFilenameForSugarWidget($class) {
            //Only bother to check if the class name starts with SugarWidget
            if (strpos($class, 'SugarWidgetField') !== false) {
                //We need to lowercase the portion after SugarWidgetField
                $name = substr($class, 16);
                if (empty($name)) {
                    return false;
                }
                $class = 'SugarWidgetField' . strtolower($name);
                return self::requireWithCustom("include/generic/SugarWidgets/{$class}.php");
            }
            return false;
        }
    */
    if ($file = self::getFilenameForSugarWidget($class)) {
        self::$classMap[$uclass] = $file;
        self::$classMapDirty = true;
        return true;
    }

    // Special case because it checks by ending in Layout
    /*
        protected static function getFilenameForLayoutClass($class) {
            if (substr($class, -6) == "Layout") {
                return self::requireWithCustom("include/MetaDataManager/layouts/$class.php");
            }
            return false;
        }
    */
    if ($file = self::getFilenameForLayoutClass($class)) {
        self::$classMap[$uclass] = $file;
        self::$classMapDirty = true;
        return true;
    }

    /*
        protected static function getFilenameForExpressionClass($class) {
            if (substr($class, -10) == 'Expression') {
                if ($file = self::requireWithCustom("include/Expressions/Expression/{$class}.php")) {
                    return $file;
                }

                $types = array("Boolean", "Date", "Enum", "Generic", "Numeric", "Relationship", "String", "Time");

                foreach ($types as $type) {
                    if ($file = self::requireWithCustom("include/Expressions/Expression/{$type}/{$class}.php")) {
                        return $file;
                    }
                }
            }
            return false;
        }
    */
    if ($file = self::getFilenameForExpressionClass($class)) {
        self::$classMap[$uclass] = $file;
        self::$classMapDirty = true;
        return true;
    }

    // Try known dirs
    /*
        public static $dirMap = array(
            'clients/base/api/',
            "data/visibility/",
            "data/Relationships/",
            "data/duplicatecheck/",
            'include/api/',
            "include/CalendarEvents/",
            "include/SugarSearchEngine/",
            "include/",
            "modules/Mailer/",
            'modules/Calendar/',
        );
    */
    foreach (self::$dirMap as $dir) {
        // include/Class.php
        if ($file = self::requireWithCustom("{$dir}$class_file.php")) {
            self::$classMap[$uclass] = $file;
            self::$classMapDirty = true;
            return true;
        }
        // include/Class/Class.php
        // Note here we don't use $class_file since using path twice would not make sense:
        // Foo/Bar/Foo/Bar.php vs. Foo_Bar/Foo_Bar.php
        if ($file = self::requireWithCustom("{$dir}$class/$class.php")) {
            self::$classMap[$uclass] = $file;
            self::$classMapDirty = true;
            return true;
        }
        // try include/Foo_Bar.php as a last resort
        if ($file = self::requireWithCustom("{$dir}$class.php")) {
            self::$classMap[$uclass] = $file;
            self::$classMapDirty = true;
            return true;
        }
    }

    self::$classMap[$uclass] = false;
    self::$classMapDirty = true;
    return false;
}


        分析完了init,基本上auotoloader.php文件也大体结束了,剩余的就是相关引入文件操作了,SugarCRM里也有相关简短说明,可以看看。

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