公用`Laravel`框架与公用库架构

前言

终于有那么点时间能将Laravel 5的一些好的实践总结出来,希望为普及Laravel和新的PHP编程思想出一份力。如有错误或你有更好的方式,请不吝赐教,共同进步。

本文有配套的git仓库,你也可以clone我的代码仓库,里面包含每一步的操作(没有多余步骤)。

git clone git@git.oschina.net:notfound/Separated-Laravel.git

正文

通常很多项目都会依赖同一个的框架,还会共用很多代码库,手动复制粘贴这些文件到每个项目文件夹显然很伤害键盘,特别当项目多了之后,手动管理各种不同版本的库极容易精神分裂。为避免让搬砖这项工作对身体、精神造成双重伤害,最好将这些文件公用化。那么有哪些方法呢?

使用Composer

Composer是什么?ComposerPHP库的管理工具。简单来说就是所有库都要告诉Composer自己依赖哪些库,这样当你告诉Composer你需要哪些库(甚至特定的版本)的时候,Composer就可以把你指定的库以及他们的依赖帮你全部下载到项目中。

很多人都用过老版本的ThinkPHP或者CodeIgniter,那么一定对importvendor$this->load这些函数记忆犹新,他们经常在构造方法里成群出现,形成一道靓丽的风景线。那些日子可以忘掉了。使用Composer,只要遵循PSR-0PSR-4规范,即可实现自动加载。

Laravel官方倡导使用Composer来管理项目(新建Laravel项目都是用的Composer,让很多人感到不适应)。使用Composer只需在项目目录下的composer.json文件中注明依赖库的名字、版本,一个composer install命令即可自动下载,并且这些库自身的依赖也会被自动处理。以下是一个典型Laravel 5新项目的composer.json

{
    "name": "laravel/laravel",
    "description": "The Laravel Framework.",
    "keywords": ["framework", "laravel"],
    "license": "MIT",
    "type": "project",
    "require": {
        "laravel/framework": "5.0.*"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.0",
        "phpspec/phpspec": "~2.1"
    },
    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/TestCase.php"
        ]
    },
    "scripts": {
        "post-install-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-update-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-create-project-cmd": [
            "php -r \"copy('.env.example', '.env');\"",
            "php artisan key:generate"
        ]
    },
    "config": {
        "preferred-install": "dist"
    }
}

这里就不细说Composer的使用方法和配置格式了。

只需要理解一点,composer.json就是你项目所需要的库的清单(Laravel框架也是一个Composer库),composer install命令则会查找当前目录下的清单,然后自动下载这些库到当前目录的vendor/文件夹(如果本地已经下载过相同版本,则直接从缓存读取),并且生成一个autoload.php文件,然后你只需要require这个文件,即可调用安装的库了(autoload.php里实现了一个懒加载,在调用未声明类时,会按照自己的规则去引用这个文件)。

注意,在一个典型的Laravel项目中,你并不需要手动require这个文件,入口文件引用的/bootstrap/autoload.php里已经包含。

简单介绍了Composer,相信已经有人想到怎么用Composer来管理公用代码:只需将所有用到的公用代码封装成库就行了。如果不想提交到Composer官方的源,我们也可以在内网搭建一个公用库的Composer服务器。

每个提交都会产生一个Composer版本(便于管理),Composer如果检测到本地有相同版本的缓存文件,安装速度也会非常快,不必太担心速度问题。但这个方法显得稍繁琐了。在修改库之后必须通过Composer更新到源,接着依赖的项目还得一个个执行composer update来更新(你可以写自动化脚本,不过就更麻烦了吧)。

在一个中小型项目中,我们可能只会维护一套框架版本(例如Laravel 5.*)和一套公用代码库,那么在每个项目中都安装一次Laravel框架和代码库总让人觉得有点不对劲。而且Composer的官方源因为某些神秘原因而非常慢,有时新建一个Laravel项目需要20分钟……我们不想浪费团队每个人的时间,我们试试有没有别的解决方案。

链接法

这是我总结的一套方案,目前工作得还不错。在构建共用库目录结构之前,我们得先把Laravel框架公用出来,因为我们只需要一套能公用的框架代码。

我们先从一个标准Laravel项目中分离出Laravel框架。我知道有些人表示很担心,所以首先确定几个基本原则

  1. 不改变Laravel项目的目录结构、不要改动框架代码,方便未来升级;

  2. 不用奇怪的hack方式实现(通用性不强);

  3. 不会给新建项目带来一些配置麻烦(比如得通过ln -s映射一些目录);

  4. 没有任何功能遗失(我们想感受Laravel所有的优点)。

OK,明确了基本原则,我们来看看设想的、分离之后的目录结构:

application/
laravel/

application是项目目录,laravelLaravel框架的目录,清晰明了。

我们再来看看一个官方Laravel项目的目录结构(使用Laravel 5.0.16):

Laravel官方新项目结构

app/
bootstrap/
config/
database/
public/
resources/
storage/
tests/
vendor/
.env
.env.example
.gitattributes
.gitignore
artisan
composer.json
composer.lock
gulpfile.js
package.json
phpspec.yml
phpunit.xml
readme.md
server.php

如果你在使用ThinkPHPCodeIgniter等没有采用composer等技术的框架,看到这么多不认识的文件肯定不开心了……不过不要紧,这并不妨碍构建一个基础Hello world实例(当然还是得下载Composer),其他的东西你可以搜索网络了解,或者看我以后的教程分享(如果有时间写的话)。

文件夹和文件看起来很多,我们一个个来看吧。要分离出框架,首先我们要弄清楚什么是不能分离出去、必须放在项目文件夹里的。

不能分离的文件、目录

这是一份我总结的列表和原因:

app/                #项目的程序逻辑总不能拿出去吧?
bootstrap/          #我们稍后单独说
config/             #项目配置,你懂的
database/           #项目的数据库相关脚本
public/             #项目的,入口文件`index.php`我们单独说
resources/          #项目的资源
storage/            #项目的本地存储
tests/              #项目的测试脚本,删掉也不影响
vendor/             #稍后单独说
.env                #也是项目的配置,在`Laravel`文档中有说明
.env.example        #是上面文件的好基友
.gitattributes      #框架的,移走
.gitignore          #框架的,移走
artisan             #稍后单独说
composer.json       #稍后单独说
composer.lock       #稍后单独说
gulpfile.js         #项目的,不细说,删掉也不影响
package.json        #项目的,不细说,删掉也不影响
phpspec.yml         #项目的,不细说,删掉也不影响
phpunit.xml         #项目的,不细说,删掉也不影响
readme.md           #框架的README,移走
server.php          #稍后单独说

我们已经排除了一大半不能移动的文件(文件夹)。我们来单独看几个特殊的。

artisan

artisanLaravel的特色之一,如果想要在项目目录执行php artisan [command],这个得保留。打开看看它的代码:

#!/usr/bin/env php
<?php

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/

require __DIR__.'/bootstrap/autoload.php';

$app = require_once __DIR__.'/bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/

$kernel = $app->make('Illuminate\Contracts\Console\Kernel');

$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);

/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running. We will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/

$kernel->terminate($input, $status);

exit($status);

基本来说它就是一个入口文件,将处理逻辑丢给了Laravel核心,所以它基本不会改变,我们可以放心留下它(require路径问题我们后面接着说)。

server.php

<?php
/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell <taylorotwell@gmail.com>
 */

$uri = urldecode(
    parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH)
);

// This file allows us to emulate Apache's "mod_rewrite" functionality from the
// built-in PHP web server. This provides a convenient way to test a Laravel
// application without having installed a "real" web server software here.
if ($uri !== '/' and file_exists(__DIR__.'/public'.$uri))
{
    return false;
}

require_once __DIR__.'/public/index.php';

使用PHP内置web服务器启动这个脚本可以进行快速调试(无需Http服务器),文件结构一目了然,无需更改。

readme.md

放在laravel文件夹,饮水思源,尊重劳动成果。

Composer文件、bootstrap/、vendor、public/index.php

这几个部分涉及了整个框架的加载流程,所以放在一起说。

分离Laravel框架,我们得知道Laravel框架在哪吧。既然是通过Composer安装的,那肯定在vendor/文件夹,我们把它移到我们自己的laravel/文件夹不就完了!然而这并没有什么用……

当我们打开vendor/,发现:

bin/
classpreloader/
composer/
danielstjules/
dnoegel/
doctrine/
...
symfony/
vlucas/
autoload.php

怎么这么多文件!可是composer.jsonrequire部分明明是这样:

...
    "require": {
        "laravel/framework": "5.0.*"
    },
    "require-dev": {
        "phpunit/phpunit": "~4.0",
        "phpspec/phpspec": "~2.1"
    },
...

没错,这是一个官方Laravel项目的依赖列表,除开Composer自身产生的文件(composer/autoload.php),应该只有3个目录才对,其他的是什么呢?

其实,其他的文件夹是项目依赖的依赖Composer默认都会放到顶层的vendor/文件夹(和拖家带口的npm的明显差别)。

那我们是不是把这些文件夹全部移到我们的laravel/文件夹就行了呢?且慢。

Composer

我们继续看看根目录的composer.json文件。

...
    "require-dev": {
        "phpunit/phpunit": "~4.0",
        "phpspec/phpspec": "~2.1"
    },
...

Laravel默认自带了phpspec.ymlphpunit.xml,两者都是代码测试工具的配置文件,所以默认也带上了这两个开发依赖(不影响项目正常运行的依赖)。Laravel官方还有一些扩展包,也是通过Composer安装的,更有Laravel开发者喜闻乐见的ide-helper(一个为Facade特性增加代码补全功能的库),都需要通过Composer安装。特别不要忘了,我们的Laravel还要通过Composer来升级啊,所以我们最好保留Composer需要的结构,所以现在我们的laravel/文件夹是这样的:

laravel/
    bootstrap/
    vendor/
    composer.json
    composer.lock
    .gitattributes
    .gitignore
    README.md

我们将vendor/composer.json原样保存,在项目中只需要引入vendor/autoload.php就可以自动加载框架了,这样无论是升级Laravel还是composer install安装任何需要共用的包都非常容易。

但是请注意composer.json的这一段:

...
    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/TestCase.php"
        ]
    },
    "scripts": {
        "post-install-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-update-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-create-project-cmd": [
            "php -r \"copy('.env.example', '.env');\"",
            "php artisan key:generate"
        ]
    },
...

这里都是针对项目的配置,不删掉会造成报错。那么我们改成:

...
    "autoload": {
        "classmap": [
            
        ],
        "psr-4": {
            
        }
    },
    "autoload-dev": {
        "classmap": [
            
        ]
    },
    "scripts": {
        "post-install-cmd": [
            
        ],
        "post-update-cmd": [
            
        ],
        "post-create-project-cmd": [
            
        ]
    },
...

我们的项目文件也需要依赖Composer来实现例如自动加载等功能,所以我们在application/文件夹下创建一个新的composer.json文件,内容如下:

{
    "name": "application",
    "description": "my application.",
    "keywords": [],
    "license": "MIT",
    "type": "project",
    "require": {

    },
    "require-dev": {

    },
    "autoload": {
        "classmap": [
            "database"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
    "autoload-dev": {
        "classmap": [
            "tests/TestCase.php"
        ]
    },
    "scripts": {
        "post-install-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-update-cmd": [
            "php artisan clear-compiled",
            "php artisan optimize"
        ],
        "post-create-project-cmd": [
            "php -r \"copy('.env.example', '.env');\"",
            "php artisan key:generate"
        ]
    },
    "config": {
        "preferred-install": "dist"
    }
}

接着在application/目录中执行composer dumpautoload以生成自动加载的相关文件。

public/index.phpbootstrap/

大块头都移走了,我们再从入口文件开始看:

<?php
/**
 * Laravel - A PHP Framework For Web Artisans
 *
 * @package  Laravel
 * @author   Taylor Otwell <taylorotwell@gmail.com>
 */

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels nice to relax.
|
*/

require __DIR__.'/../bootstrap/autoload.php';

/*
|--------------------------------------------------------------------------
| Turn On The Lights
|--------------------------------------------------------------------------
|
| We need to illuminate PHP development, so let us turn on the lights.
| This bootstraps the framework and gets it ready for use, then it
| will load up this application so that we can run it and send
| the responses back to the browser and delight our users.
|
*/

$app = require_once __DIR__.'/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can simply call the run method,
| which will execute the request and send the response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make('Illuminate\Contracts\Http\Kernel');

$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);

代码意图很明确,加载bootstrap/下的两个文件,分别实现自动加载(懒加载)设置框架,在public/index.php的最后,启动了框架流程。这两个文件我们也移到laravel/bootstrap/文件夹,不过需要解决一下路径问题。

例如bootstrap/autoload.php文件里是这样的:

<?php

define('LARAVEL_START', microtime(true));

/*
|--------------------------------------------------------------------------
| Register The Composer Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/

require __DIR__.'/../vendor/autoload.php';

/*
|--------------------------------------------------------------------------
| Include The Compiled Class File
|--------------------------------------------------------------------------
|
| To dramatically increase your application's performance, you may use a
| compiled class file which contains all of the classes commonly used
| by a request. The Artisan "optimize" is used to create this file.
|
*/

$compiledPath = __DIR__.'/../storage/framework/compiled.php';

if (file_exists($compiledPath))
{
    require $compiledPath;
}

30行指定了storage/framework/compiled.php(编译命令产生的缓存文件,用来提高性能),storage/文件夹是属于项目的,那么我们在public/index.php里定义一个项目文件夹路径:

// 项目文件夹
define('APP_DIR', __DIR__);

然后将bootstrap/autoload.php30行改为:

$compiledPath = APP_DIR.'/../storage/framework/compiled.php';

完美。bootstrap/下的文件并不涉及到Laravel核心逻辑,我也不认为在5.*版本(起码也是5.1以内)中会有太大变化,所以放心改。我们再看看bootstrap/app.php

<?php

/*
|--------------------------------------------------------------------------
| Create The Application
|--------------------------------------------------------------------------
|
| The first thing we will do is create a new Laravel application instance
| which serves as the "glue" for all the components of Laravel, and is
| the IoC container for the system binding all of the various parts.
|
*/

$app = new Illuminate\Foundation\Application(
    realpath(__DIR__.'/../')
);

/*
|--------------------------------------------------------------------------
| Bind Important Interfaces
|--------------------------------------------------------------------------
|
| Next, we need to bind some important interfaces into the container so
| we will be able to resolve them when needed. The kernels serve the
| incoming requests to this application from both the web and CLI.
|
*/

$app->singleton(
    'Illuminate\Contracts\Http\Kernel',
    'App\Http\Kernel'
);

$app->singleton(
    'Illuminate\Contracts\Console\Kernel',
    'App\Console\Kernel'
);

$app->singleton(
    'Illuminate\Contracts\Debug\ExceptionHandler',
    'App\Exceptions\Handler'
);

/*
|--------------------------------------------------------------------------
| Return The Application
|--------------------------------------------------------------------------
|
| This script returns the application instance. The instance is given to
| the calling script so we can separate the building of the instances
| from the actual running of the application and sending responses.
|
*/

return $app;

同样,第15行的__DIR.'/../'指的是项目的根目录,所以我们改成:

$app = new Illuminate\Foundation\Application(
    realpath(APP_DIR)
);

最后,我们将public/index.php里的引用代码改成为laravel/bootstrap/下的这两个文件就可以了,我们定义一个常量LARAVEL_DIR指向laravel/文件夹以便我们写路径。

对了,差点忘记还得在public/index.php开头加上require __DIR__.'/../vendor/autoload.php'

到这里,我们的项目就可以正常运行了。

等一下!是不是漏了什么

对了,还有最开始看的artisan文件。我们再打开看看:

#!/usr/bin/env php
<?php

/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/

require __DIR__.'/bootstrap/autoload.php';

$app = require_once __DIR__.'/bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/

$kernel = $app->make('Illuminate\Contracts\Console\Kernel');

$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);

/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running. We will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/

$kernel->terminate($input, $status);

exit($status);

这里也引用了bootstrap/,但是我们已经把它移到laravel/了,我们就修改一下吧。

但是这里改一下那里改一下,也太乱了吧……

那么优化一下。

我们先不动artisan并还原application/public/index.php,然后在项目目录下也添加一个bootstrap/目录,添加bootstrap/autoload.phpbootstrap/app.php两个文件,文件内容很简单,直接引用laravel/bootstrap/下对应的两个文件,并把require __DIR__.'/../vendor/autoload.php'放在这里的autoload.php中。当然我们还是要定义LARAVEL_DIRAPP_DIR,我们在项目根目录下新建一个path.php文件,把路径定义放进去,然后在public/index.phpartisan里加上对path.php的引用就大功告成了。

对原始文件的改动少多了,你的代码洁癖症有没有感觉好一些?

就这样完了?这样肯定有问题!

目前我确实发现了一个问题。

php artisan optimize命令

在执行php artisan optimize命令的时候会出现错误:

[InvalidArgumentException]                                                                                                                                          
  Configuration file "/application_1/,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,  
  ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,/application_1/app/Providers/App  
  ServiceProvider.php,/application_1/app/Providers/BusServiceProvider.php,/application_1/app/Providers/ConfigSer  
  viceProvider.php,/application_1/app/Providers/EventServiceProvider.php,/application_1/app/Providers/RouteServi  
  ceProvider.php" does not exist.  

这是因为optimize命令里写死了框架核心文件的路径,必须从$app['path.base'].'vendor/'里加载,而$app['path.base']则是指向的项目根目录(我觉得这样写死很不科学,不过这是传说中的规范。Laravel官方并不推荐将框架分离出去,所以基于这个出发点,写死做法也没有问题)。

我们可以单独实现一个optimize命令来解决这个问题(我的就叫做optimize-separated),或者我们在laravel/目录下执行composer dumpautoload -o,也能获得差不多的性能优化。

优化

程序可以正常运行了,不过我们还得优化一下结构,

Laravel是一个更新非常频繁、社区非常活跃的框架,这意味着版本更新会很快。版本升级通常会有一些目录结构的改变(3到4,4到5,变化都很大),有些是推荐性的、有些是强制性的,所以一年后我们的laravel/可能在用Laravel 6了,项目文件结构发生了很大的改变,而我们并不想去修改一年前项目的结构,假设我们使用的是LTS(长期支持)版本,我们也不需要紧跟最新的大版本。所以我们做一个简单的修改。

application/
laravel/
    laravel5.0/

laravel/的文件移到laravel5.0/即可,以后升级了我们就再开一个目录,例如laravel5.1/

最后将laravel/laravel5.0/vendor/文件夹从laravel/laravel5.0/.gitignore中移除,提交到版本控制服务器,团队中其他人只需拉取你提交的框架而不用执行composer install了。框架代码最好由一个人维护,以免造成代码冲突。

公用库

篇幅有限,这里只讲一下解决方案。我们主要利用命名空间Composer来实现。

首先需要修改laravel/laravel5.0/composer.json文件的这一段:

...
    "autoload-dev": {
        "classmap": [
            
        ]
    },
...

我们添加一个配置:

...
    "autoload": {
        "classmap": [
            
        ],
        "psr-4": {
            "Common\\": "../common/"
        }
    },
...

这里主要使用了PSR-4规范(和application/composer.json一样。具体规范这里就不细说了)。

那么我们就可以开始写公用库了!新建文件laravel/common/Add.php,输入以下内容:

<?php namespace Common;

class Add
{
    
    static function execute($a, $b)
    {
        return $a + $b;
    }
    
}

然后在laravel/laravel5.0/目录下执行命令生成新的自动加载配置:

composer dumpautoload

接下来我们就可以直接使用\\Common\\Add::execute(1, 2)了。我这里将common/文件夹放在了laravel/文件夹中,如果你的代码库和Laravel某个版本有依赖关系,那么放在指定版本的Laravel文件夹中更科学。

因为包含了一点点推理,也许本文会让人觉得有点复杂,你可以参考文章开头的git仓库,里面仅包含直接有效的操作步骤和说明。

这篇文章就这么完了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266

推荐阅读更多精彩内容

  • 是什么 如果你知道yum、apt-get、npm、bower等命令中的一种或者多种,那么,你也能很快知道compo...
    旱魃一样阅读 3,077评论 0 9
  • 先说几句废话,调和气氛。事情的起由来自客户需求频繁变更,伟大的师傅决定横刀立马的改革使用新的框架(created ...
    wsdadan阅读 3,005评论 0 12
  • Laravel框架笔记 一、 composer的安装: 1.Composer是什么?是 PHP 用来管理依赖(de...
    李景磊阅读 859评论 0 4
  • 原文地址:http://www.insp.top/learn-laravel-container 本系列文章主要是...
    Bensontung阅读 1,092评论 0 4
  • 1. 准备 本节将使用 bugging 程序,首先确认之前有执行过以下命令 建立32位可执行程序编译环境$ sud...
    小虾米不爱吃虾阅读 288评论 0 0