Laravel源码--Facade门面

文章简单介绍了 Facade 门面,总结了其工作原理,并介绍了 Facade 的使用方法。

介绍

Facade 为应用服务容器中绑定的类提供了一个“静态”接口。Laravel 内置了很多 Facade,几乎可以用来访问 Laravel 中所有的服务。Laravel 的 Facade 作为服务容器中底层类的“静态代理”,相比于传统静态方法,Facade 提供了简洁且丰富的语法同时,还带来了更好的可测试性和扩展性。Laravel 的所有 Facade 都定义在 “Illuminate\Support\Facades”命名空间下,我们也可以自己定义新的 Facade。

Facade 加载原理

为了更好的理解 Facade 的工作原理,我们从以下几个方面进行介绍:

  • Facade 配置文件
  • 加载 RegisterFacades 类
  • 注册 Facade 服务
  • 解析 Facade 服务

Facade配置文件

Facade 的配置文件保存在 config/app.php 中,主要用来保存所有的 Facade 别名,也即是把 Facade 和别名的对应关系存放在 config/app.php文件中的 aliases 数组里。aliases 数组的形式如下:


    'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Artisan' => Illuminate\Support\Facades\Artisan::class,
        'Auth' => Illuminate\Support\Facades\Auth::class,
        'Blade' => Illuminate\Support\Facades\Blade::class,
        'Broadcast' => Illuminate\Support\Facades\Broadcast::class,
        'Bus' => Illuminate\Support\Facades\Bus::class,
        'Cache' => Illuminate\Support\Facades\Cache::class,
        'Config' => Illuminate\Support\Facades\Config::class,
        'Cookie' => Illuminate\Support\Facades\Cookie::class,
        'Crypt' => Illuminate\Support\Facades\Crypt::class,
        'DB' => Illuminate\Support\Facades\DB::class,
        'Eloquent' => Illuminate\Database\Eloquent\Model::class,
        'Event' => Illuminate\Support\Facades\Event::class,
        'File' => Illuminate\Support\Facades\File::class,
        'Gate' => Illuminate\Support\Facades\Gate::class,
        'Hash' => Illuminate\Support\Facades\Hash::class,
        'Lang' => Illuminate\Support\Facades\Lang::class,
        'Log' => Illuminate\Support\Facades\Log::class,
        'Mail' => Illuminate\Support\Facades\Mail::class,
        'Notification' => Illuminate\Support\Facades\Notification::class,
        'Password' => Illuminate\Support\Facades\Password::class,
        'Queue' => Illuminate\Support\Facades\Queue::class,
        'Redirect' => Illuminate\Support\Facades\Redirect::class,
        'Redis' => Illuminate\Support\Facades\Redis::class,
        'Request' => Illuminate\Support\Facades\Request::class,
        'Response' => Illuminate\Support\Facades\Response::class,
        'Route' => Illuminate\Support\Facades\Route::class,
        'Schema' => Illuminate\Support\Facades\Schema::class,
        'Session' => Illuminate\Support\Facades\Session::class,
        'Storage' => Illuminate\Support\Facades\Storage::class,
        'URL' => Illuminate\Support\Facades\URL::class,
        'Validator' => Illuminate\Support\Facades\Validator::class,
        'View' => Illuminate\Support\Facades\View::class,

    ],

aliases 数组遵循(别名 => Facade 类)的数据格式,当接受一个 HTTP 请求时注册 aliases 数组,但是并没有实际的进行别名绑定,只是通过 spl_autoload_register() 函数进行了注册。只有在遇到尚未发现的别名时,才去真正绑定别名,因此,别名绑定是“懒惰”加载,并不影响程序的性能。

加载 RegisterFacades 类

Facade 服务的注册工作由“Illuminate\Foundation\Bootstrap\RegisterFacades”类完成,RegisterFacades 类是在启动应用程序的过程中加载的。因此,我们首先来看index.php文件

<?php
//public\index.php
/**
 * 记录框架的启动时间
 * microtime(get_as_float)函数返回当前Unix时间戳的微妙数
 * get_as_float为true时返回浮点数,为false时返回字符串,默认为false
 */
define('LARAVEL_START', microtime(true));

/**
 * Composer的自动加载文件
 */
require __DIR__.'/../vendor/autoload.php';

/**
 * bootstrap文件里创建了一个Application实例
 */
$app = require_once __DIR__.'/../bootstrap/app.php';

/**
 * 通过container服务容器获得一个kernel类的实例,(Illuminate\Contracts\Http\Kernel是一个接口,
 * 真正生成的实例是App\Http\Kernel类)
 */
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);


/**
 * laravel中所有功能服务的注册加载
 * 通过调用kernel的handle方法,返回一个response
 * Illuminate\Http\Request::capture():通过全局$_SERVER数组构造一个Http请求的语句
 */
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

//把response内容发到浏览器
$response->send();

//执行请求生命周期中的后续操作
$kernel->terminate($request, $response);

RegisterFacades 类的加载是在 $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ) 语句进行的。该语句主要用于 Laravel各个功能服务的注册启动。

$request = Illuminate\Http\Request::capture() 

该语句是 Laravel 通过全局 $_SERVER 数组构造一个 HTTP 请求的语句,接下来会调用 HTTP 的内核函数 handle(),RegisterFacades 类的加载即是在内核函数 handle() 中进行。

//Illuminate\Foundation\Http\Kernel.php
    /**
     * Handle an incoming HTTP request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function handle($request)
    {
        try {
            //允许在表单中使用delete、put等类型的请求
            $request->enableHttpMethodParameterOverride();

            //将请求发给中间件、路由处理
            $response = $this->sendRequestThroughRouter($request);
        } catch (Exception $e) {
            $this->reportException($e);

            $response = $this->renderException($request, $e);
        } catch (Throwable $e) {
            $this->reportException($e = new FatalThrowableError($e));

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->dispatch(
            new Events\RequestHandled($request, $response)
        );

        return $response;
    }

接下来,我们查看 sendRequestThroughRouter() 函数。

//Illuminate\Foundation\Http\Kernel.php
     /**
     * Send the given request through the middleware / router.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        //设置request请求的对象实例
        $this->app->instance('request', $request);

        //清楚'request'对应的服务对象实例
        Facade::clearResolvedInstance('request');

        /**
         * 依次执行$bootstrappers中每一个bootstrapper的bootstrap()函数,做了几件准备事情:
         * 1. 配置加载 LoadConfiguration
         * 2.日志配置 ConfigureLogging
         * 3.异常处理 HandleException
         * 4.注册Facades RegisterFacades
         * 5.注册Providers RegisterProviders
         * 6.启动Providers BootProviders
         */
        $this->bootstrap();

        return (new Pipeline($this->app))       //请求的分发
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

函数首先在 Laravel 容器中设置了 request 请求的对象实例,并且清楚了 Facade 中 request 请求对应的服务对象实例。接下来我们来看 bootstrap() 函数

//Illuminate\Foundation\Http\Kernel.php
     /**
     * Bootstrap the application for HTTP requests.
     * 启动引导(reauests驱动)
     *
     * @return void
     */
    public function bootstrap()
    {
        //判断引导是否已经被启动
        if (! $this->app->hasBeenBootstrapped()) {
            /**
             * 使用Application类的bootstrapWith()函数启动引导
             * 参数:bootstrapers数组。
             */
            $this->app->bootstrapWith($this->bootstrappers());
        }
    }

     /**
     * The bootstrap classes for the application.
     * 引导类,起引导作用的类
     *
     * @var array
     */
    protected $bootstrappers = [
        //载入服务器环境变量(.env文件)
        \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
        //载入配置信息(config目录)
        \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
        //配置异常处理
        \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
        //注册Facades
        \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
        //注册Providers
        \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
        //启动Providers
        \Illuminate\Foundation\Bootstrap\BootProviders::class,
    ];

bootstrap() 函数首先判断引导类是否已经启动,如果没有启动,则启动引导类。引导类数组包含 RegisterFacades 类。启动引导类是调用“Illuminate\Foundation\Application”类中的 bootstrapWith() 函数。

//Illuminate\Foundation\Application
     /**
     * Run the given array of bootstrap classes.
     *
     * @param  array  $bootstrappers
     * @return void
     */
    public function bootstrapWith(array $bootstrappers)
    {
        $this->hasBeenBootstrapped = true;

        foreach ($bootstrappers as $bootstrapper) {
            /**
             * 告知将要启动该bootstrapper
             * $this['events']:对应绑定的类为 Dispatcher类(Illuminate\Events\Dispatcher)
             * [$this]:数组,只有一个Application类元素
             */
            $this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);

            //解析每个 $bootstrapper,并调用他们自身的 bootstrap() 函数
            $this->make($bootstrapper)->bootstrap($this);

            $this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
        }
    }

bootstrapWith() 函数中的 $this->make($bootstrapper)->bootstrap($this) 语句即是解析出 $bootstrappers 数组中的每个类,并调用相应的 bootstrap() 函数。因此,RegisterFacades 类也即是在此时调用。

注册 Facade 服务

下面我们来看 RegisterFacades 类。

//Illuminate\Foundation\Bootstrap\RegisterFacades.php
<?php

namespace Illuminate\Foundation\Bootstrap;

use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Facades\Facade;
use Illuminate\Foundation\PackageManifest;
use Illuminate\Contracts\Foundation\Application;

class RegisterFacades
{
    /**
     * Bootstrap the given application.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return void
     */
    public function bootstrap(Application $app)
    {
        //清除所有的门面对象服务实例
        Facade::clearResolvedInstances();

        //设置门面对象的Application实例
        Facade::setFacadeApplication($app);

        /**
         * 默认的别名配置是从 app 配置文件下的 aliases 读取的,
         * PackageManifest 是 laravel 5.5 新增的 包自动发现 规则
         */
        AliasLoader::getInstance(array_merge(
            $app->make('config')->get('app.aliases', []),
            $app->make(PackageManifest::class)->aliases()
        ))->register();
    }
}

可以看出,RegisterFacades 类只有一个 bootstrap() 函数,该函数主要完成以下功能:

  • 清楚所有 Facade 对象服务实例
  • 设置门面对象的 Application 实例
  • 通过 AliasLoader 类为所有的 Facade 注册别名

接下来我们查看 AliasLoader 类如何注册别名。

//llluminate\Foundation\AliasLoader.php
     /**
     * Get or create the singleton alias loader instance.
     * 获取或创建 AliasLoader 单例实例。
     *
     * @param  array  $aliases
     * @return \Illuminate\Foundation\AliasLoader
     */
    public static function getInstance(array $aliases = [])
    {
        if (is_null(static::$instance)) {
            return static::$instance = new static($aliases);
        }

        $aliases = array_merge(static::$instance->getAliases(), $aliases);

        static::$instance->setAliases($aliases);

        return static::$instance;
    }

     /**
     * Register the loader on the auto-loader stack.
     * 将加载器注册到自动加载中
     *
     * @return void
     */
    public function register()
    {
        if (! $this->registered) {
            $this->prependToLoaderStack();

            $this->registered = true;
        }
    }

     /**
     * Prepend the load method to the auto-loader stack.
     * 设置自动加载方法
     *
     * @return void
     */
    protected function prependToLoaderStack()
    {
        // 把AliasLoader::load()放入自动加载函数队列中,并置于队列头部
        spl_autoload_register([$this, 'load'], true, true);
    }

AliasLoader 类首先调用 getInstance() 函数获得 AliasLoader 的单例实例,然后调用 register() 函数将 AliasLoader 类中的 load() 函数注册到 SPL __autoload 函数队列的头部。

//llluminate\Foundation\AliasLoader.php
     /**
     * Load a class alias if it is registered.
     *
     * @param  string  $alias
     * @return bool|null
     */
    public function load($alias)
    {
        if (static::$facadeNamespace && strpos($alias, static::$facadeNamespace) === 0) {
            $this->loadFacade($alias);

            return true;
        }

        if (isset($this->aliases[$alias])) {
            //注册别名
            return class_alias($this->aliases[$alias], $alias);
        }
    }

load() 函数的上半部分是 laravel5.4 版本新出的功能,叫做实时门面服务。下半部分是 class_alias() 函数利用别名映射数组将别名映射到真正的门面类中去。例如,我们使用别名类 Cache 时,程序会通过 AliasLoader 类的 load() 函数为“Illuminate\Support\Facades\Cache::class”类创建一个别名 Cache,所以我们在程序里使用别名 Cache 就是使用“Illuminate\Support\Facades\Cache”类。

解析 Facade 服务

以 Cache 为例,我们讲一下 Facade 的使用。我们首先来看一下 Cache Facade 类。

<?php
//Illuminate\Support\Facades\Cache.php

class Cache extends Facade
{
    /**
     * Get the registered name of the component.
     * 获取组件注册名称
     *
     * @return string
     */
    protected static function getFacadeAccessor()
    {
        return 'cache';
    }
}

该类内部只有一个 getFacadeAccessor() 函数,该方法的功能是获取已经注册组件的名称。其实每个门面类只是重写了基类(Facade)的 getFacadeAccessor() 方法。

//Illuminate\Support\Facades\Facade.php
     /**
     * Handle dynamic, static calls to the object.
     * 动态绑定,将门面的静态方法调用绑定到门面对应的服务对象实例来执行
     *
     * @param  string  $method
     * @param  array   $args
     * @return mixed
     *
     * @throws \RuntimeException
     */
    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);
    }

当运行 Cache::get() 函数时,门面类 Cache 类里并没有 get() 函数,此时 PHP 会自动调用魔术函数 __callStatic()。魔术函数 __callStatic() 首先获得 Facade 的服务对象实例,然后调用对象的 get() 函数。下面我们来看如何获得 Facade 的对象实例。

//Illuminate\Support\Facades\Facade.php
     /**
     * Get the root object behind the facade.
     * 返回当前门面对应服务对象的实例
     *
     * @return mixed
     */
    public static function getFacadeRoot()
    {
        return static::resolveFacadeInstance(static::getFacadeAccessor());
    }

   /**
     * Get the registered name of the component.
     * 获取组件注册名称,子类重写该函数
     *
     * @return string
     *
     * @throws \RuntimeException
     */
    protected static function getFacadeAccessor()
    {
        throw new RuntimeException('Facade does not implement getFacadeAccessor method.');
    }

     /**
     * Resolve the facade root instance from the container.
     * 创建并返回 $name 对应的对象实例
     *
     * @param  string|object  $name
     * @return mixed
     */
    protected static function resolveFacadeInstance($name)
    {
        //如果是对象,则直接返回
        if (is_object($name)) {
            return $name;
        }

        //$resolvedInstance数组中 $name 对应的对象实例是否存在,如果存在,直接返回
        if (isset(static::$resolvedInstance[$name])) {
            return static::$resolvedInstance[$name];
        }

        /**
         * 具体的创建工作由Application类对象进行,所以$name需要在Application中进行过绑定
         */
        return static::$resolvedInstance[$name] = static::$app[$name];
    }

基类中的 getFacadeRoot() 函数首先调用了 getFacadeAccessor() 函数,也即是 Cache 类重写的 getFacadeAccessor() 函数,如果调用的是基类的 getFacadeAccessor() 函数则报错。然后调用 resolveFacadeInstance() 函数获得“cache”对应的服务实例。resolveFacadeInstance() 函数主要是从 Laravel 服务容器 static::$app[$name] 中解析出相应的服务。“cache”服务是应用程序初始化时,在类“Illuminate\Foundation\Bootstrap\RegisterProviders”中被注册到容器中的。
因为在程序启动时,将 Facade 的别名通过 RegisterFacades 类进行了加载注册,所以可以直接使用别名 Cache 代替“Illuminate\Support\Facades\Cache”。
其实,Cache::get('key') 的写法等价于以下写法。

$value = $app->make('cache')->get('key');

建立 Facade

建立自己的 Facade,需要做以下几方面的工作:

  • 创建自定义类(Facade 的实现类)
  • 创建 Facade 类
  • Facade 类别名设置
  • 创建 ServiceProvider
  • 加载 ServiceProvider

创建自定义类(Facade 的实现类)

我们现在 app\Facades 目录下创建 PaymentGateway\Payment 类。

<?php
//app\Facades\\PaymentGateway\Payment.php

namespace App\Facades\PaymentGateway;

class Payment
{
    public function get(){
        return "Facade Test ...";
    }

}

创建 Facade 类

创建自定义的 Facade 类,该类继承基类 Facade,并重写基类的 getFacadeAccessor() 函数。

<?php
//app\Facades\\PaymentGateway\Facade\Payment.php

namespace App\Facades\PaymentGateway\Facade;
use Illuminate\Support\Facades\Facade;

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

Facade 类别名设置

我们可以为自定义的 Facade 类起个别名,只需要将别名和类的对应关系添加到 config/app.php 配置文件中的 aliases 数组即可。

//config/app.php

  'aliases' => [
        ...
        'Payment' => App\Facades\PaymentGateway\Facade\Payment::class,
        ...
    ],

创建 ServiceProvider

在 app\Providers 文件夹下创建 PaymentServiceProvider,提供“payment”服务。

<?php
//app\Providers\PaymentServiceProvider.php

namespace App\Providers;

use App\Facades\PaymentGateway\Payment;
use Illuminate\Support\ServiceProvider;

class PaymentServiceProvider extends ServiceProvider
{
    /**
     * Indicates if loading of the provider is deferred.
     * $defer为true,表示延迟加载此类
     *
     * @var bool
     */
    protected $defer = true;

    /**
     * Register the service provider.
     *
     * @return void
     */
    public function register()
    {
        $this->app->singleton('payment', function ($app) {
            return new Payment();
        });

    }

    /**
     * 设置了延迟启动,需要重写 providers 函数
     * 该方法返回provider中注册的服务容器绑定别名
     */
    public function provides()
    {
        return ["payment"];
    }
}

其中,$defer 设置为 true,是为了提供延迟加载功能,provides() 函数也是为了辅助延迟加载功能而重写的。register() 函数中的服务名“payment”应该与 Facade 类中 getFacadeAccessor() 函数返回值保持一致。

加载 ServiceProvider

为了能加载我们创建的 PaymentServiceProvider,需要在 config/app.php 配置文件中 providers 数组内添加 PaymentServiceProvider。

//config/app.php

    'providers' => [
        ...
        App\Providers\PaymentServiceProvider::class,
        ...
 ],

最后,使用如下代码即可以调用 Facade 类。

$value = Payment::get();

总结

之所以能够简单的使用 Cache::get() 操作,是因为程序启动时自动进行了别名注册,使用 Cache 等价于“Illuminate\Support\Facades\Cache”。另外,程序在基类 Facade 中自动调用魔术函数 __callStatic() 进行了动态绑定。

门面类列表

下面列出了每个 Facade 、对应的底层类以及服务容器绑定键。

门面 Facade 类 Class 服务容器绑定
App Illuminate\Foundation\Application app
Artisan Illuminate\Console\Application artisan
Auth Illuminate\Auth\AuthManager auth
Auth (实例) Illuminate\Auth\Guard
Blade Illuminate\View\Compilers\BladeCompiler blade.compiler
Bus Illuminate\Contracts\Bus\Dispatcher
Cache Illuminate\Cache\CacheManager cache
Config Illuminate\Config\Repository config
Cookie Illuminate\Cookie\CookieJar cookie
Crypt Illuminate\Encryption\Encrypter encrypter
DB Illuminate\Database\DatabaseManager db
DB (实例) Illuminate\Database\Connection
Event Illuminate\Events\Dispatcher events
File Illuminate\Filesystem\Filesystem files
Hash Illuminate\Contracts\Hashing\Hasher hash
Input Illuminate\Http\Request request
Lang Illuminate\Translation\Translator translator
Log Illuminate\Log\Writer log
Mail Illuminate\Mail\Mailer mailer
Password Illuminate\Auth\Passwords\PasswordBroker auth.password
Queue Illuminate\Queue\QueueManager queue
Queue (实例) Illuminate\Queue\QueueInterface
Queue (基础类) Illuminate\Queue\Queue
Redirect Illuminate\Routing\Redirector redirect
Redis Illuminate\Redis\Database redis
Request Illuminate\Http\Request request
Response Illuminate\Contracts\Routing\ResponseFactory
Route Illuminate\Routing\Router router
Schema Illuminate\Database\Schema\Blueprint
Session Illuminate\Session\SessionManager session
Session (实例) Illuminate\Session\Store
Storage Illuminate\Contracts\Filesystem\Factory filesystem
URL Illuminate\Routing\UrlGenerator url
Validator Illuminate\Validation\Factory validator
Validator (实例) Illuminate\Validation\Validator
View Illuminate\View\Factory view
View (实例) Illuminate\View\View
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容