• public/index.php Kernel 接收 Requeset ,返回 Response
  • BeforeMiddleWare + Route + AfterMiddleWare, 前置中间件:检查请求,处理参数; Route 路由转发; 后置中间件:处理数据,后续动作
  • Controller + Action, 参数校验,调用service,返回
  • Request 定义入参、校验逻辑、错误信息
  • Service 处理业务(事务),调用 Repository,格式化数据,触发事件监听
  • Repository 处理 SQL增删改查, 建议 1个Repository注入多个Model,处理相关数据(如:父表-子表,数据表-日志表)
  • Model定义映射表

运行原理

入口文件 ./public/index.php

# composer自动加载
require __DIR__.'/../vendor/autoload.php';

# 获取laravel核心的Ioc容器
$app = require_once __DIR__.'/../bootstrap/app.php';

# "make"出Http请求的内核
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

# laravel里面所有功能服务的注册加载,乃至Http请求的构造
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

# 发送请求
$response->send();

# 执行,返回
$kernel->terminate($request, $response);

Composer自动加载

  • 类的自动加载(autoload)机制 解决手动 require/include 造成的遗漏、包含不必要文件的问题。
  • Lazy loading (延迟加载) :在使用类时才自动包含类文件,而不是一开始就将所有的类文件
  • PHP 自动加载函数 __autoload()
bool  spl_autoload_register ( [callback $autoload_function] )    
接受两个参数:一个是添加到自动加载栈的函数,另外一个是加载器不能找到这个类时是否抛出异常的标志。
第一个参数是可选的,并且默认指向spl_autoload()函数,
这个函数会自动在路径中查找具有小写类名和.php扩展或者.ini扩展名,
或者任何注册到spl_autoload_extensions()函数中的其它扩展名的文件。

如果你所在的代码位置访问不了 $app 变量,可以使用辅助函数resolve:

$api = resolve('HelpSpot\API');

IOC容器

Laravel 服务容器是一个用于管理类依赖和执行依赖注入的强大工具。

  • 服务容器就是工厂模式的升级版,工厂解耦了对象和外部资源之间的关系,但是和外部资源之间存在在耦和
  • 服务容器在为对象创建了外部资源的同时,又与外部资源没有任何关系,这个就是 Ioc 容器
  • 只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于【依赖注入(DI)】
【依赖注入】是从【应用程序的角度】在描述:应用程序依赖容器创建并注入它所需要的外部资源;
【控制反转】是从【容器的角度】在描述,:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

Laravel服务容器主要承担两个作用:绑定与解析。

自动注入

namespace App\Http\Controllers;
use App\Users\Repository as UserRepository;
class UserController extends Controller
{
    /**
     * 用户仓库实例
    */
    protected $users;
    /**
     * 创建一个控制器实例
    *
    * @param UserRepository $users 自动注入
    * @return void
    */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }
}

call方法注入

class TaskRepository{
    public function testContainerCall(User $user,Task $task){
        $this->assertInstanceOf(User::class, $user);
        $this->assertInstanceOf(Task::class, $task);
    }
    public static function testContainerCallStatic(User $user,Task $task){
        $this->assertInstanceOf(User::class, $user);
        $this->assertInstanceOf(Task::class, $task);
    }
    public function testCallback(){
        echo 'call callback successfully!';
    }
    public function testDefaultMethod(){
        echo 'default Method successfully!';
    }
}

闭包函数注入

public function testCallWithDependencies()
{
      $container = new Container;
      $result = $container->call(function (StdClass $foo, $bar = []) {
          return func_get_args();
      });
      $this->assertInstanceOf('stdClass', $result[0]);
      $this->assertEquals([], $result[1]);
      $result = $container->call(function (StdClass $foo, $bar = []) {
          return func_get_args();
      }, ['bar' => 'taylor']);
      $this->assertInstanceOf('stdClass', $result[0]);
      $this->assertEquals('taylor', $result[1]);
}

Facade门面

门面相对于其他方法来说,最大的特点就是简洁

  • Laravel 自带了很多 facades ,几乎可以用来访问到 Laravel 中所有的服务。
  • Laravel facades 实际上是服务容器中那些底层类的「静态代理」,相比于传统的静态方法, facades 在提供了简洁且丰富的语法同时,还带来了更好的可测试性和扩展性。
App::make('router')->get('/path', 'PathController@actionName');
# 使用门面模式方式:
Route::get('/path', 'PathController@actionName')
# 门面最后调用的函数也是服务容器的make函数

当 门面没有指定静态函数时,PHP就会调用魔术函数__callStatic

abstract class Facade
{
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

    protected static function resolveFacadeInstance($name)
    {
        if (is_object($name)) {
            return $name;
        }

        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        # 门面最后调用的函数也是服务容器的make函数
        return static::$resolvedInstance[$name] = static::$app[$name];
    }

    public static function __callStatic($method, $args)
    {
        $instance = static::getFacadeRoot();

        if (! $instance) {
            throw new RuntimeException('A facade root has not been set.');
        }

        return $instance->$method(...$args);
    }   
}

每个门面类也就是重定义一下getFacadeAccessor函数

class DB extends Facade
{
    /**
     * Get the registered name of the component.
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'db';
    }
}

中间件

  • 最好将中间件设想为一系列「层」HTTP 请求在到达您的应用程序之前必须通过。每一层都可以检查请求,甚至完全拒绝它。