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!";
}
}
代码解析
上面的代码可以实现最简单的,类自加载.基本原理
- 先看index.php spl_autoload_register("Loader::autoload"); 将Loader::autoload注册,作为__autoload的实现.相当于一旦调用类不存在,则会自动调用Loader::autoload函数.
- 再看Loader.php 该类下包含autoload函数,这个函数已由1注册,函数仅包含一行代码
require DIR . '/' . str_replace('\\', '/', $class) . '.php';
这段代码将类名,转化为类所在地址,并引入
综合上面两步就可完成类自加载.当然这只是最简单的自加载(仅实现PSR-0部分要求,关于psr感兴趣的同学可以自行搜索).仅能后实现我们自己的代码,对于第三方代码,无法实现.
用laravel的同学一定知道composer,composer当中的类命名空间都是作者提前写好的,我们无需更改引入包的代码,就可以将包内的类直接使用.这里使用的就是PSR-4代码规范.对于规范内容本文不做过多解释.我们开始真正的研究laravel类自加载的代码实现.
- 真正的实现,用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的类自加载方式分为以下四种:
- PRS-0加载-对应的加载文件autoload_namespaces.php
- PRS-4加载-对应的加载文件autoload_psr4.php
- 不遵守PSR-0与PSR-4类加载-对应的加载文件autoload_classmap.php
- 公有方法加载-对应的加载文件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包的教程,在里面会对这些做一个更加详细的阐述.
网友评论