美文网首页
laravel源码学习(1)-从自加载开始

laravel源码学习(1)-从自加载开始

作者: guijianshi | 来源:发表于2018-02-26 10:30 被阅读0次

    laravel源码学习(1)-从自加载开始

    前言

    工作一年多,框架用过symfony,thinkphp,laravel.很多时候都只是在用而不了解框架内部的运行,最近在看完《大话设计模式》和《深入php面向对象,模式与实现》之后想将所看到的内容做一个融汇过程.便想到研究框架.laravel框架目前最流行的php框架就是首选.本系列教程主要从框架设计的角度解读laravel.

    正文

    所有的框架首要要实现的就是自加载,然后用命名空间的方式代替一大堆的require.工作后发现仍有许多php对命名空间,类自加载一窍不通,故而将类自加载作为第一篇

    类自加载的实现

    在讲原理之前我们先看一下代码


    目录

    -- test/Test.php
    -- index.php
    -- Loader.php
    

    index.php

    <?php
    define('DIR', __DIR__);
    include_once DIR . '/Loader.php';
    spl_autoload_register('Loader::autoload');
    $test = new \test\Test();
    $test->index();
    

    Loader.php

    <?php
    
    class Loader
    {
        public static function autoload($class)
        {
            require DIR . '/' . str_replace('\\', '/', $class) . '.php';
        }
    }
    

    Test.php

    <?php
    namespace test;
    
    
    class Test
    {
        public function index()
        {
            echo "Hello World!";
        }
    }
    

    代码解析
    上面的代码可以实现最简单的,类自加载.基本原理

    1. 先看index.php spl_autoload_register("Loader::autoload"); 将Loader::autoload注册,作为__autoload的实现.相当于一旦调用类不存在,则会自动调用Loader::autoload函数.
    2. 再看Loader.php 该类下包含autoload函数,这个函数已由1注册,函数仅包含一行代码
    require DIR . '/' . str_replace('\\', '/', $class) . '.php';
    

    这段代码将类名,转化为类所在地址,并引入
    综合上面两步就可完成类自加载.当然这只是最简单的自加载(仅实现PSR-0部分要求,关于psr感兴趣的同学可以自行搜索).仅能后实现我们自己的代码,对于第三方代码,无法实现.
    用laravel的同学一定知道composer,composer当中的类命名空间都是作者提前写好的,我们无需更改引入包的代码,就可以将包内的类直接使用.这里使用的就是PSR-4代码规范.对于规范内容本文不做过多解释.我们开始真正的研究laravel类自加载的代码实现.

    1. 真正的实现,用phpstrom我们可以很多容易的追踪到实现代码是写在autoload_real.php当中
    <?php
    
    // autoload_real.php @generated by Composer
    
    class ComposerAutoloaderInit0fdc2c73177a176dbcd305400c8a4f4e
    {
        private static $loader;
    
        public static function loadClassLoader($class)
        {
            if ('Composer\Autoload\ClassLoader' === $class) {
                require __DIR__ . '/ClassLoader.php';
            }
        }
    
        public static function getLoader()
        {
            // 简单单例
            if (null !== self::$loader) {
                return self::$loader;
            }
            // 函数注册
            spl_autoload_register(array('ComposerAutoloaderInit0fdc2c73177a176dbcd305400c8a4f4e', 'loadClassLoader'), true, true);
            self::$loader = $loader = new \Composer\Autoload\ClassLoader();
            // 注销已注册的__autoload()函数
            spl_autoload_unregister(array('ComposerAutoloaderInit0fdc2c73177a176dbcd305400c8a4f4e', 'loadClassLoader'));
    
            $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
            if ($useStaticLoader) {
                require_once __DIR__ . '/autoload_static.php';
    
                call_user_func(\Composer\Autoload\ComposerStaticInit0fdc2c73177a176dbcd305400c8a4f4e::getInitializer($loader));
            } else {
                // PSR-0标准,设置命名空间对应路径,以便随后自动加载相关类文件
                $map = require __DIR__ . '/autoload_namespaces.php';
                foreach ($map as $namespace => $path) {
                    $loader->set($namespace, $path);
                }
                // PSR-4标准,设置命名空间对应路径,以便随后自动加载相关类文件
                $map = require __DIR__ . '/autoload_psr4.php';
                foreach ($map as $namespace => $path) {
                    $loader->setPsr4($namespace, $path);
                }
                // 设置类文件路径与类名的对应关系,以便随后自动加载相关类文件(不遵守PSR-0和PSR-4的类)
                $classMap = require __DIR__ . '/autoload_classmap.php';
                if ($classMap) {
                    $loader->addClassMap($classMap);
                }
            }
    
            // 将loader内的各种类include进来
            $loader->register(true);
    
            // 加载公有方法如helpers内定义的函数
            if ($useStaticLoader) {
                $includeFiles = Composer\Autoload\ComposerStaticInit0fdc2c73177a176dbcd305400c8a4f4e::$files;
            } else {
                $includeFiles = require __DIR__ . '/autoload_files.php';
            }
            foreach ($includeFiles as $fileIdentifier => $file) {
                composerRequire0fdc2c73177a176dbcd305400c8a4f4e($fileIdentifier, $file);
            }
    
            return $loader;
        }
    }
    
    function composerRequire0fdc2c73177a176dbcd305400c8a4f4e($fileIdentifier, $file)
    {
        if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
            require $file;
    
            $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
        }
    }
    

    简单总结

    laravel的类自加载方式分为以下四种:

    1. PRS-0加载-对应的加载文件autoload_namespaces.php
    2. PRS-4加载-对应的加载文件autoload_psr4.php
    3. 不遵守PSR-0与PSR-4类加载-对应的加载文件autoload_classmap.php
    4. 公有方法加载-对应的加载文件autoload_files.php

    自定义自动加载方式

    自定义加载方式需要在composer.json当中添加

        "autoload": {
            "psr-0" : {
                "AppPsr0": "apppsr0/"
            },
            "classmap": [
                "database/seeds",
                "database/factories"
            ],
            "psr-4": {
                "App\\": "app/"
            },
            "files" : ["common/util.php"]
        }
    

    更新完之后需要运行下方命令,命令会更新总结中描述的四个文件

    composer dump-autoload
    

    生产环境需要运行下方命令,改命令会将PSR-0和PSR-4加载的文件写入autoload_classmap里,提升加载速度.

    composer dump-atoload -o
    

    具体情况各位可以自行测试一下,而后查看对应文件.接下来笔者也会写一期开发一个composer包的教程,在里面会对这些做一个更加详细的阐述.

    相关文章

      网友评论

          本文标题:laravel源码学习(1)-从自加载开始

          本文链接:https://www.haomeiwen.com/subject/hjsexftx.html