入口:public/index.php
$app = require __DIR__.'/../bootstrap/app.php';
$app->run();
先看第一句:打开 app.php
<?php
require_once __DIR__.'/../vendor/autoload.php';
try {
(new Dotenv\Dotenv(__DIR__.'/../'))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
//
}
$app = new Qudian\Base\Application(
realpath(__DIR__.'/../')
);
$app->withAliases([
Auto\Facades\Gateway::class => 'Gateway',
]);
$app->withFacades();
$app->configure('trade');
$app->withEloquent();
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
$app->routeMiddleware([
'trace' => \Auto\Foundation\Http\Middlewares\TraceLogMiddleware::class,
]);
$app->register(App\Providers\EventServiceProvider::class);
$app->register(Auto\Providers\ServiceProvider::class);
$app->register(Laravel\Tinker\TinkerServiceProvider::class);
$app->register(Mpociot\ApiDoc\ApiDocGeneratorServiceProvider::class);
$app->register(Sentry\SentryLaravel\SentryLumenServiceProvider::class);
$app->register(Illuminate\Redis\RedisServiceProvider::class);
$app->register(Workflow\Business\Console\ConsoleServiceProvider::class);
$app->register(App\Providers\DBLogServiceProvider::class);
Queue::extend('aliyunmns', function () {
return new Stone\Queue\Connectors\AliyunMNSConnector();
});
$app->router->group([
'namespace' => 'App\Http\Controllers',
], function ($router) {
require __DIR__.'/../routes/web.php';
});
return $app;
第一句,引入composer的自动加载文件,掠过。
require_once __DIR__.'/../vendor/autoload.php';
第二句,加载根目录下面的env文件,并设置成环境变量,不展开了,科普两个函数 file() putenv()
try {
(new Dotenv\Dotenv(__DIR__.'/../'))->load();
} catch (Dotenv\Exception\InvalidPathException $e) {
//
}
file() 函数把整个文件读入一个数组中。
与 file_get_contents() 类似,不同的是 file() 将文件作为一个数组返回。数组中的每个单元都是文件中相应的一行,包括换行符在内。
如果失败,则返回 false。
putenv() 设置环境变量的值, 例如 "FOO=BAR"
第三句,
$app = new \Laravel\Lumen\Application(
realpath(__DIR__.'/../')
);
打开Application.php
public function __construct($basePath = null)
{
if (! empty(env('APP_TIMEZONE'))) {
date_default_timezone_set(env('APP_TIMEZONE', 'UTC'));
}
$this->basePath = $basePath;
$this->bootstrapContainer();
$this->registerErrorHandling();
$this->bootstrapRouter();
}
一句句看下来。
$this->bootstrapContainer();
protected function bootstrapContainer()
{
static::setInstance($this);
$this->instance('app', $this);
$this->instance('Laravel\Lumen\Application', $this);
$this->instance('path', $this->path());
$this->registerContainerAliases();
}
先看 第一句,绑定自身。
static::setInstance($this);
public static function setInstance(ContainerContract $container = null)
{
return static::$instance = $container;
}
二看 第二句
$this->instance('app', $this);
public function instance($abstract, $instance)
{
$this->removeAbstractAlias($abstract);
$isBound = $this->bound($abstract);
unset($this->aliases[$abstract]);
$this->instances[$abstract] = $instance;
if ($isBound) {
$this->rebound($abstract);
}
return $instance;
}
继续深入 查看removeAbstractAlias方法 尚未绑定 $this->aliases任何东西,直接返回。
$this->removeAbstractAlias($abstract);
protected function removeAbstractAlias($searched)
{
if (! isset($this->aliases[$searched])) {
return;
}
foreach ($this->abstractAliases as $abstract => $aliases) {
foreach ($aliases as $index => $alias) {
if ($alias == $searched) {
unset($this->abstractAliases[$abstract][$index]);
}
}
}
}
查看bound方法,返回false
$isBound = $this->bound($abstract);
public function bound($abstract)
{
return isset($this->bindings[$abstract]) ||
isset($this->instances[$abstract]) ||
$this->isAlias($abstract);
}
接下来,将Instance的第一个参数和第二个参数做了个绑定
$this->instances[$abstract] = $instance;
第四句,因为$isBound=false,返回instance。
返回 bootstrapContainer()方法,看registerContainerAliases(),赋值了$this->aliases变量
回到Application.php 构造方法的第二个语句,设置了全局的错误捕捉函数,不展开了。
$this->registerErrorHandling();
构造方法的第三句,将Router绑定到 application上
$this->bootstrapRouter();
public function bootstrapRouter()
{
$this->router = new Router($this);
}
到此为止,构造方法的函数全都讲解完毕。回到app.php文件内。
app.php 第四句
$app->withAliases([
Auto\Facades\Gateway::class => 'Gateway',
]);
public function withAliases($userAliases = [])
{
$defaults = [
'Illuminate\Support\Facades\Auth' => 'Auth',
'Illuminate\Support\Facades\Cache' => 'Cache',
'Illuminate\Support\Facades\DB' => 'DB',
'Illuminate\Support\Facades\Event' => 'Event',
'Illuminate\Support\Facades\Gate' => 'Gate',
'Illuminate\Support\Facades\Log' => 'Log',
'Illuminate\Support\Facades\Queue' => 'Queue',
'Illuminate\Support\Facades\Schema' => 'Schema',
'Illuminate\Support\Facades\URL' => 'URL',
'Illuminate\Support\Facades\Validator' => 'Validator',
];
if (! static::$aliasesRegistered) {
static::$aliasesRegistered = true;
$merged = array_merge($defaults, $userAliases);
foreach ($merged as $original => $alias) {
class_alias($original, $alias);
}
}
}
设置类别名,如果没有阅读到此处的代码,会极大增加了使用框架时候的难度,会很难理解有些类从何而来。
bool class_alias ( string $original , string $alias [, bool $autoload = TRUE ] )
基于用户定义的类 original 创建别名 alias。 这个别名类和原有的类完全相同
app.php 第五句,把当前类 $this 传入 Facade,其他功能和第四句一样。
$app->withFacades();
public function withFacades($aliases = true, $userAliases = [])
{
Facade::setFacadeApplication($this);
if ($aliases) {
$this->withAliases($userAliases);
}
}
app.php 第六句
$app->configure('trade');
public function configure($name)
{
if (isset($this->loadedConfigurations[$name])) {
return;
}
$this->loadedConfigurations[$name] = true;
$path = $this->getConfigurationPath($name);
if ($path) {
$this->make('config')->set($name, require $path);
}
}
根据name获取config文件夹下对应的文件路径 不展开了 返回的path是 config/{$name}.php
$path = $this->getConfigurationPath($name);
这句深入展开
$this->make('config')->set($name, require $path);
先看make方法
public function make($abstract, array $parameters = [])
{
$abstract = $this->getAlias($abstract);
if (array_key_exists($abstract, $this->availableBindings) &&
! array_key_exists($this->availableBindings[$abstract], $this->ranServiceBinders)) {
$this->{$method = $this->availableBindings[$abstract]}();
$this->ranServiceBinders[$method] = true;
}
return parent::make($abstract, $parameters);
}
第一步获取aliases,前面有个 registerContainerAliases()方法注册了aliases,此处传入的是config,不存在,所以直接返回config
public function getAlias($abstract)
{
if (! isset($this->aliases[$abstract])) {
return $abstract;
}
if ($this->aliases[$abstract] === $abstract) {
throw new LogicException("[{$abstract}] is aliased to itself.");
}
return $this->getAlias($this->aliases[$abstract]);
}
第二步,这里availableBindings是个预先写好的数组,返回的method是registerConfigBindings
$this->{$method = $this->availableBindings[$abstract]}();
接下来看到 registerConfigBindings()方法
protected function registerConfigBindings()
{
$this->singleton('config', function () {
return new ConfigRepository;
});
}
展开 singleton()方法说下
public function singleton($abstract, $concrete = null)
{
$this->bind($abstract, $concrete, true);
}
public function bind($abstract, $concrete = null, $shared = false)
{
$this->dropStaleInstances($abstract);
if (is_null($concrete)) {
$concrete = $abstract;
}
if (! $concrete instanceof Closure) {
$concrete = $this->getClosure($abstract, $concrete);
}
$this->bindings[$abstract] = compact('concrete', 'shared');
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
看 dropStaleInstances()方法 清除掉绑定的instance aliases
protected function dropStaleInstances($abstract)
{
unset($this->instances[$abstract], $this->aliases[$abstract]);
}
看 getClosure()方法,返回匿名函数
protected function getClosure($abstract, $concrete)
{
return function ($container, $parameters = []) use ($abstract, $concrete) {
if ($abstract == $concrete) {
return $container->build($concrete);
}
return $container->make($concrete, $parameters);
};
}
接下来 bindings'config' = 匿名函数 bindings'config' = $shared
$this->bindings[$abstract] = compact('concrete', 'shared');
这一句不做分析,此处没用到
if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
回到make方法,parent::make() 重点
public function make($abstract, array $parameters = [])
{
return $this->resolve($abstract, $parameters);
}
protected function resolve($abstract, $parameters = [])
{
$abstract = $this->getAlias($abstract);
$needsContextualBuild = ! empty($parameters) || ! is_null(
$this->getContextualConcrete($abstract)
);
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
$this->with[] = $parameters;
$concrete = $this->getConcrete($abstract);
if ($this->isBuildable($concrete, $abstract)) {
$object = $this->build($concrete);
} else {
$object = $this->make($concrete);
}
foreach ($this->getExtenders($abstract) as $extender) {
$object = $extender($object, $this);
}
if ($this->isShared($abstract) && ! $needsContextualBuild) {
$this->instances[$abstract] = $object;
}
$this->fireResolvingCallbacks($abstract, $object);
$this->resolved[$abstract] = true;
array_pop($this->with);
return $object;
}
获取aliases别名 没有别名 返回的还是config
$abstract = $this->getAlias($abstract);
中间几句略过,没用到,看到这句,这里是获取在getClosure()方法时绑定的匿名函数
$concrete = $this->getConcrete($abstract);
接下来,返回的是true
$this->isBuildable($concrete, $abstract)
protected function isBuildable($concrete, $abstract)
{
return $concrete === $abstract || $concrete instanceof Closure;
}
那么就进入到
$object = $this->build($concrete);
public function build($concrete)
{
// If the concrete type is actually a Closure, we will just execute it and
// hand back the results of the functions, which allows functions to be
// used as resolvers for more fine-tuned resolution of these objects.
if ($concrete instanceof Closure) {
return $concrete($this, $this->getLastParameterOverride());
}
$reflector = new ReflectionClass($concrete);
// If the type is not instantiable, the developer is attempting to resolve
// an abstract type such as an Interface of Abstract Class and there is
// no binding registered for the abstractions so we need to bail out.
if (! $reflector->isInstantiable()) {
return $this->notInstantiable($concrete);
}
$this->buildStack[] = $concrete;
$constructor = $reflector->getConstructor();
// If there are no constructors, that means there are no dependencies then
// we can just resolve the instances of the objects right away, without
// resolving any other types or dependencies out of these containers.
if (is_null($constructor)) {
array_pop($this->buildStack);
return new $concrete;
}
$dependencies = $constructor->getParameters();
// Once we have all the constructor's parameters we can create each of the
// dependency instances and then use the reflection instances to make a
// new instance of this class, injecting the created dependencies in.
$instances = $this->resolveDependencies(
$dependencies
);
array_pop($this->buildStack);
return $reflector->newInstanceArgs($instances);
}
因为concrete是匿名函数
if ($concrete instanceof Closure) {
return $concrete($this, $this->getLastParameterOverride());
}
//也就是此处返回的是
function () {
return new ConfigRepository;
});
回到Application.php $this->make('config') 返回的是 ConfigRepository
$this->make('config')->set($name, require $path);
为 ConfigRepository 里面的 $this->items 赋值
一开始有个疑问,如果每次都make,items也不是静态变量,为什么可以一直留存在内存中。
后面发现,application的instances变量,如果已经存在,就不会重新走一遍流程
if (isset($this->instances[$abstract]) && ! $needsContextualBuild) {
return $this->instances[$abstract];
}
回到 app.php,上面的make已经讲过了。
$app->withEloquent();
public function withEloquent()
{
$this->make('db');
}
接下来,singleton上面已经讲过了
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
$app->singleton(
Illuminate\Contracts\Console\Kernel::class,
App\Console\Kernel::class
);
接下来,中间件,下次讲解
$app->routeMiddleware([
'trace' => \Auto\Foundation\Http\Middlewares\TraceLogMiddleware::class,
]);
接下来,注册,没什么难度,略过
$app->register(App\Providers\EventServiceProvider::class);
public function register($provider)
{
if (! $provider instanceof ServiceProvider) {
$provider = new $provider($this);
}
if (array_key_exists($providerName = get_class($provider), $this->loadedProviders)) {
return;
}
$this->loadedProviders[$providerName] = true;
if (method_exists($provider, 'register')) {
$provider->register();
}
if (method_exists($provider, 'boot')) {
return $this->call([$provider, 'boot']);
}
}
接下来,此处的Queue很难理解从哪里引入,其实就是上面的class_alia函数,设置了别名。
Queue::extend('aliyunmns', function () {
return new Stone\Queue\Connectors\AliyunMNSConnector();
});
接下来,这里的route 是 Application.php 构造方法里的 $this->bootstrapRouter() 函数赋值的,路由的时候展开讲解。
$app->router->group([
'namespace' => 'App\Http\Controllers',
], function ($router) {
require __DIR__.'/../routes/web.php';
});
至此,app.php的初始化完成。
回到index.php,第二篇开始讲解路由
$app->run();