Lumen 利用 Composer 来管理它的代码依赖,所以在使用 Lumen 之前,请先确认你的电脑上安装了 Composer。
看官请绕道: Lumen 中文文档 5.7 ,此篇博客是本人复习+学习笔记。
安装
环境要求
- PHP >= 7.1.3
- OpenSSL PHP Extension
- PDO PHP Extension
- Mbstring PHP Extension
安装Lumen
1 2 3 4 5 6
| composer global require "laravel/lumen-installer" lumen new site
composer create-project --prefer-dist laravel/lumen site
|
运行
1
| php -S localhost:8000 -t public
|
应用程序密钥
安装完 Lumen 后,首先需要做的事情是设置一个随机字符串到应用程序密钥。通常这个密钥会有 32 字符长。
Laravel 有自动生成的命令: php artisan key:generate
如果应用程序密钥没有被设置的话,你的用户 Session 和其它的加密数据都是不安全的!
配置
获取/设置配置
1 2 3 4 5 6
| // 读取 $value = config('app.locale'); // 设置 config(['app.locale' => 'en']); // 使用前加载 在'boostrap/app.php' $app->configure('app');
|
环境配置
Lumen
利用 Vance Lucas
的 PHP
库 DotEnv
使得此项功能的实现变得非常简单。
1 2 3 4 5
| try { (new Dotenv\Dotenv(dirname(__DIR__)))->load(); } catch (Dotenv\Exception\InvalidPathException $e) { }
|
创建应用时应该将 .env.example
文件重命名为 .env
.env
文件中列出的所有变量都会被加载到 PHP
的超全局变量 $ _ENV
中。 env
函数可以用来获取环境变量中的值
1
| $debug = env('APP_DEBUG', true);
|
判定当前运行环境
1 2 3 4 5 6 7 8 9 10
| $environment = app()->environment();
if (app()->environment('local')) { }
if (app()->environment('local', 'staging')) { }
|
路由
基本路由
1 2 3
| $router->get('foo', function () { return 'Hello World'; });
|
可供使用的路由方法
1 2 3 4 5 6
| $router->get($uri, $callback); $router->post($uri, $callback); $router->put($uri, $callback); $router->patch($uri, $callback); $router->delete($uri, $callback); $router->options($uri, $callback);
|
路由参数
必填参数
1 2 3 4 5 6 7
| $router->get('user/{id}', function ($id) { return 'User ' . $id; });
$router->get('posts/{postId}/comments/{commentId}', function ($postId, $commentId) { // });
|
可选参数
1 2 3
| $router->get('user[/{name}]', function ($name = null) { return $name; })
|
命名路由
1 2 3 4 5 6 7 8
| $router->get('profile', ['as' => 'profile', function () { });
$router->get('profile', [ 'as' => 'profile', 'uses' => 'UserController@showProfile' ]);
|
中间件/命名空间/路由前缀
1 2 3 4 5 6 7 8 9
| $router->group([ 'middleware' => 'auth', 'namespace' => 'Admin', 'prefix' => 'admin' ], function () use ($router) { $router->get('/', function () { // 使用Auth中间件 }); });
|
中间件
HTTP 中间件提供了一个方便的机制来过滤进入应用程序的 HTTP 请求。
例如,Lumen 内置了一个中间件来验证用户的身份认证。如果用户未通过身份验证,中间件将会把用户导向登录页面,反之,当用户通过了身份验证,中间件将会通过此请求并接着往下执行。
当然,除了身份验证之外,中间件也可以被用来运行各式各样的任务,
如:CORS 中间件负责替所有即将离开程序的响应加入适当的标头;而日志中间件则可以记录所有传入应用程序的请求。
定义中间件
可以通过复制 Lumen
内置的示例文件 ExampleMiddleware
来创建一个中间件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| <?php
namespace App\Http\Middleware;
use Closure;
class OldMiddleware {
public function handle($request, Closure $next) { if ($request->input('age') <= 200) { return redirect('home'); } return $next($request); } }
|
前置/后置中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
namespace App\Http\Middleware;
use Closure;
class BeforeMiddleware { public function handle($request, Closure $next) {
return $next($request); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
namespace App\Http\Middleware;
use Closure;
class AfterMiddleware { public function handle($request, Closure $next) { $response = $next($request);
return $response; } }
|
注册中间件
全局中间件
只需要在 bootstrap/app.php
文件中的 $app->middleware()
方法中列出这个中间件:
1 2 3
| $app->middleware([ App\Http\Middleware\OldMiddleware::class ]);
|
为路由分配中间件
首先需要在 bootstrap/app.php
文件中调用 $app->routeMiddleware()
方法时为中间件分配一个简短的键:
1 2 3
| $app->routeMiddleware([ 'auth' => App\Http\Middleware\Authenticate::class, ]);
|
一旦在 HTTP
内核中定义好了中间件,就可以在路由选项内使用 middleware
键:
1 2 3 4 5 6 7 8 9
| $router->get('admin/profile', ['middleware' => 'auth'], function () { });
$router->get('/', ['middleware' => ['first', 'second'], function () { }]);
|
中间件参数
中间件也可以接收自定义传参,例如,要在运行特定操作前检查已验证用户是否具备该操作的 “ 角色 “ ,可以创建 RoleMiddleware
来接收角色名称作为额外的传参。
附加的中间件参数将会在 $next
参数之后被传入中间件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?php
namespace App\Http\Middleware;
use Closure;
class RoleMiddleware {
public function handle($request, Closure $next, $role) { if (! $request->user()->hasRole($role)) { }
return $next($request); }
}
|
在路由中可使用冒号 :
来区隔中间件名称与指派参数,多个参数可使用逗号作为分隔:
1 2 3
| $router->put('post/{id}', ['middleware' => 'role:editor'], function ($id) { });
|
Terminable 中间件
有时中间件可能需要在 HTTP 响应发送到浏览器之后处理一些工作。例如, "session"
中间件会在响应发送到浏览器之后将会话数据写入存储器中。
想要做到这一点,你需要定义一个名为 "terminable"
的中间并添加一个 terminate
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
namespace Illuminate\Session\Middleware;
use Closure;
class StartSession { public function handle($request, Closure $next) { return $next($request); }
public function terminate($request, $response) { } }
|
terminate
方法应该同时接收和响应。一旦定义了这个中间件,你应该将它添加到路由列表或 bootstrap/app.php
文件的全局中间件中。
控制器
基础控制器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php
namespace App\Http\Controllers;
use App\User;
class UserController extends Controller {
public function show($id) { return User::findOrFail($id); } }
|
然后像下面这样将路由指向控制器的方法:
1
| $router->get('user/{id}', 'UserController@show');
|
控制器中间件
中间件 可通过如下方式分配到路由中:
1 2 3 4
| $router->get('profile', [ 'middleware' => 'auth', 'uses' => 'UserController@showProfile' ]);
|
更方便的方式是在控制器的构造方法里面使用 middleware
方法指定中间件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public function __construct() { $this->middleware('auth'); $this->middleware('log', ['only' => [ 'fooAction', 'barAction', ]]); $this->middleware('subscribed', ['except' => [ 'fooAction', 'barAction', ]]); }
|
依赖注入
构造器注入
Lumen
使用 「 服务容器 」 来解析所有的控制器的依赖注入。
因此,你可以在控制器的构造函数中使用类型提示需要的任何依赖。这些依赖将会自动的解析并注入到控制器实例中:
1 2 3 4 5 6
| use App\Repositories\UserRepository;
public function __construct(UserRepository $ur) { $this->users = $ur; }
|
方法注入
除了构造器注入以外,你也可以在你的控制器方法中使用类型提示依赖项。
例如,在某个方法中添加 Illuminate\Http\Request
实例的类型提示:
1 2 3 4 5 6 7 8 9 10 11 12 13
| use Illuminate\Http\Request;
public function store(Request $request) { $name = $request->input('name'); }
public function update(Request $request, $id) { }
|
请求
获取请求实例
要通过依赖注入的方式获取当前HTTP请求的实例,你应该在控制器构造函数或方法中使用 Illuminate\Http\Request
类,当前请求实例将自动被 服务容器 注入:
同方法注入.
获取请求的URI
1 2 3 4 5 6 7 8 9 10 11 12
| $uri = $request->path();
if ($request->is('admin/*')) { }
$url = $request->url();
$url = $request->fullUrl();
|
获取请求的方法
1 2 3 4 5 6
| $method = $request->method();
if ($request->isMethod('post')) { }
|
PSR-7 请求
PSR-7
标准规定了 HTTP
消息接口包含了请求及响应,如果你想获得 PSR-7
的请求实例,就需要先安装几个库,
Laravel
使用 Symfony
的 HTTP
消息桥接组件,将原 Laravel
的请求及响应转换至 PSR-7
所支持的实现:
1 2
| composer require symfony/psr-http-message-bridge composer require zendframework/zend=diactoros
|
安装完这些库后,你就可以在路由或控制器中,简单的对请求类型使用类型提示来获取 PSR-7 请求:
1 2 3 4
| user Psr\Http\Message\ServerRequestInterface; $router->get('/', function (ServerRequestInterface $request) { });
|
获取输入数据
1 2 3 4 5 6 7 8
| $name = $request->input('name');
$name = $request->input('name', 'Caoxl');
$name = $request->input('array.0.name'); $name = $request->input('array.*.name');
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| if ($request->has('name')) { }
if ($request->has(['name', 'email'])) { }
if ($request->filled('name')) { }
|
1
| $input = $reuest->all();
|
1 2 3 4
| $input = $request->only(['username', 'password']); $input = $request->only('username', 'password'); $input = $request->except(['credit_card']); $input = $request->except('credit_card');
|
文件上传
1 2 3 4 5 6
| $file = $request->file('photo');
if ($request->hasFile('photo')) { }
|
1 2 3
| if ($request->file('photo')->isValid()) { }
|
1 2
| $request->file('photo')->move($destinationPath); $request->file('photo')->move($destinationPath, $fileName);
|
响应
基本响应
1 2 3
| $router->get('/', function () { return 'Hello World'; });
|
响应对象
1 2 3 4 5 6 7 8 9 10 11 12
| use Illuminate\Http\Response;
$router->get('home', function () { return (new Response($content, $status)) ->header('Content-Type', $value); });
$router->get('home', function () { return response($content, $ststus) ->header('Content-Type', $value); });
|
附加标头至响应
1 2 3 4 5 6 7 8 9 10 11 12
| return response($content) ->header('Content-Type', $type) ->header('X-header-One', 'Header Value') ->header('X-header-Two', 'Header Value')
return response($content) ->withHeaders([ 'Content-Type' => $type, 'X-Header-One' => 'Header Value', 'X-Header-Two' => 'Header Value', ]);
|
JSON响应
json
方法会自动将标头的 Content-Type
设置为 application/json
,并通过 PHP 的 json_encode
函数将指定的数组转换为 JSON:
1 2 3 4 5 6 7 8 9 10 11 12 13
| return response()->json(['name' => 'Abigail', 'state' => 'CA']);
return response()->json( ['error' => 'Unauthorized'], 401, ['X-Header-One' => 'Header Value'] );
return response() ->json(['name' => 'Abigail', 'state' => 'CA']) ->setCallback($reqeust->input('callback'));
|
文件下载
1 2
| return response()->download($pathToFile); return response()->download($pathToFile, $name, $headers);
|
重定向
1 2 3 4 5 6 7 8 9 10 11 12
| $router->get('dashboard', function () { return redirect('home/dashboard'); });
return redirect()->route('login');
return redirect()->route('profile', ['id' => 1]);
return redirect()->route('profile', [$user]);
|
用户认证
认证服务提供者
注意: 在使用 Lumen
的认证功能前,请取消 bootstrap/app.php
文件中的 AuthServiceProvider
调用代码的注释。
1 2 3
| $this->app['auth']->viaRequest('api',function ($request) { });
|
获取已认证的用户信息
就像 Laravel 框架一样,你可以使用 Auth::user()
方法获取当前用户,
或者,你可以在 Illuminate\Http\Request
实例上使用 $request->user()
方法:
1 2 3 4 5 6 7
| use Illuminate\Http\Request;
$router->get('/post/{id}', ['middleware' => 'auth'], function (Request $request, $id) { $user = Auth::user(); $user = $request->user(); })
|
注意: 如果你想使用 Auth::user()
来获取当前的认证用户,你需要去掉 bootstrap/app.php
文件中 $app->withFacades()
方法调用的注释。
当然,所有你想要认证的路由,都可以分配给 auth
中间件 ,但需要你去掉 bootstrap/app.php
文件中对 $app->routeMiddleware()
调用的注释:
1 2 3
| $app->routeMiddleware([ 'auth' => App\Http\Middleware\Authenticate::class, ]);
|
用户授权
与 Laravel 的差异
定义权限
在 Lumen 中,你可以很方便的在你的 AuthServiceProvider
文件中使用 Gate facade
来定义你的权限
1 2 3
| Gate::define('update-post', function ($user. $post) { return $user->id === $post->user_id; });
|
定义授权策略
和 Laravel 不一样的是,Lumen 的 AuthServiceProvider
中没有 $policies
数组。
但是你可以在 AuthServiceProvider
的 boot
方法里使用 Gate facade
来调用 policy
方法:
1
| Gate::policy(Post::class, PostPolicy::class);
|
检查权限
你可以像 Laravel 一样使用 Gate
facade 来检查权限,首先你要确保你的 bootstrap/app.php
文件开启了 facade
。
1 2 3 4 5 6 7
| if (Gate::allows('update-post', $post)) { }
if (Gate::denies('update-post', $post)) { abort(403); }
|
当然你也可以传递 User
实例来检查他的权限:
1 2 3 4 5 6 7
| if ($request->user()->can('update-post', $post)) { }
if ($request->user()->cannot('update-post', $post)) { abort(403); }
|
数据库
基本用法
注意: 如果你想使用 DB
Facade,你应该去掉在 bootstrap/app.php
文件中 $app->withFacades()
的调用的注释。
例如,在不启用 Facades
时,你可以通过 app
辅助函数连接数据库:
1
| $results = app('db')->select("SELECT * FROM users");
|
或者,在启用 Facades
后,你可以通过 DB
Facade 来连接数据库:
1
| $results = DB::select("SELECT * FROM users");
|
加密与解密
配置
在使用 Lumen
的加解密前,你应该先把 .env 文件中的 APP_KEY
选项设置为 32
位随机字符串。如果没有适当地设置这个值,所有被 Lumen 加密的值都将是不安全的。
加密
你可以使用 Crypt
门面来加密一个值。所有的加密值都使用 OpenSSL
和 AES-256-CBC
来进行加密。
此外, 所有加密过的值都会使用消息认证码 (MAC)来进行签名,以检测加密字符串是否被篡改过 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <?php
namespace App\Http\Controllers;
use App\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Crypt;
class UserController extends Controller { public function storeSecret(Request $request, $id) { $user = User::findOrFail($id); $user->fill(['secret' => Crypt::encrypt($request->secret)])->save(); } }
|
解密
当然,你可以使用 Crypt
门面的 decrypt
方法来解密值。如果该值不能够被正确的解密,
例如当 MAC(消息认证码)无效时,就会抛出异常 Illuminate\Contracts\Encryption\DecryptException
:
1 2 3 4 5 6 7
| use Illuminate\Contracts\Encryption\DescryptException;
try { $decrypted = Crypt::decrypt($encryptedValue); } catch (DecryptException $e) { }
|
错误日志
重写日志
首先在bootstrap\app.php
中修改:
1 2 3
| $app = new App\Application( realpath(__DIR__.'/../') );
|
然后再修改Application
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?php
namespace App;
use Laravel\Lumen\Application as LumenBase; use Monolog\Formatter\LineFormatter; use Monolog\Handler\StreamHandler; use Monolog\Logger;
class Application extends LumenBase { protected function getMonologHandler() { return ( new StreamHandler(storage_path(env( 'APP_LOG_PATH', 'logs/' . date('Y-m-d') . '.log' )), Logger::DEBUG )) ->setFormatter(new LineFormatter(null, null, true, true)); } }
|
事件
生成器
Lumen 中没有可以用来生成事件和监听器的命令,你可以通过简单复制 ExampleEvent
或者 ExampleListener
文件来定义你自己的事件和监听器,这两个示例文件提供了每个事件和监听器的基础类结构。
注册事件或监听器
像 Laravel 框架一样,Lumen 应用内置的 EventServiceProvider
提供了一个注册所有事件监听器的地方。 listen 属性是一个数组,它包含了所有的事件(键)和监听器(值)。所以,你可以根据应用程序的需要添加事件到这个数组:
1 2 3 4 5
| protect $listen = [ 'App\Events\ExampleEvent' => [ 'App\Listeners\ExampleListener', ], ];
|
触发事件
你可以使用 event
辅助函数或者 Event
门面在 Lumen 应用程序中触发事件。同样,这些函数的行为与 Laravel 框架一致:
1 2 3
| event(new ExampleEvent);
Event::dispatch(new ExampleEvent);
|
队列
Lumen 不包括用于自动创建新 Job
类的生成器。因此你需要复制框架所带的 ExampleJob
类。
这个类提供了每个 Job 类共享的基本结构。 ExampleJob
所继承的 Job 基类已包含所需的 InteractsWithQueue
、Queueable
和 SerializesModels trait
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <?php
namespace App\Jobs;
class ExampleJob extends Job { public function __construct() { } public function handle() { } }
|
调度作业
1
| dispatch(new ExampleJob);
|
当然,你也可以使用 Queue
facade。如果你选择使用 facade,请务必在 bootstrap/app.php
文件中取消对 $app->withFacades()
调用的注释:
1
| Queue::push(new ExampleJob);
|
服务容器
Laravel 的服务容器是一个管理类依赖和执行依赖注入的强力工具。
依赖注入是个花俏的名词,事实上是指:类的依赖通过构造器或在某些情况下通过「setter」
方法「注入」。
获取服务容器
Laravel\Lumen\Application
实例是 Illuminate\Container\Container
的扩展,所以你可以当做服务容器来使用。
通常我们会在 服务提供者 注册我们的容器解析规则。
解析实例
1
| $instance = app(Something::class);
|
服务提供者
服务提供者是所有 Lumen 应用程序启动的中心所在。包括你自己的应用程序,以及所有的 Lumen 核心服务,都是通过服务提供者启动的。
编写服务提供者
所有的服务提供者都继承了 Illuminate\Support\ServiceProvider
这个类。
这个抽象类要求你在你的提供者上定义至少一个方法:register
。
在 register
方法内,你应该 只需要将事物绑定到服务容器中。
永远不要试图在 register
方法中注册任何事件侦听器、路由或任何其它功能。
注册方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
namespace App\Providers;
use Tiak\Connection; use Illuminate\Support\ServiceProvider;
class RiakServiceProvider extends ServiceProvider { public function register() { $this->app->singelton(Connection::class, function ($app) { return new Connection(config('riak')); }); } }
|
启动方法
此方法在所有其他服务提供者都注册之后才能调用,也就意味着可以访问已经被框架注册的所有服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php
namespace App\Providers;
use Queue; use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider { public function boot() { Queue::failing(function ($event) { }); } }
|
注册服务提供者
所有的服务提供者在 bootstrap/app.php
文件中被注册。这个文件中包含对 $app->register()
方法调用。
你也可以额外的调用 $app->register()
来注册你的服务提供者。
测试
测试环境
在运行测试时,Lumen 自动配置讲缓存驱动配置为 array
,意味着在测试的时候不会保存任何的缓存数据。
你可以随意创建其他必要的测试配置环境。 testing
的环境变量可以在 phpunit.xml
文件中进行修改
定义和运行测试
要创建一个测试用例,直接将新的测试文件创建到 tests
文件夹下即可。测试文件必须继承 TestCase
1 2 3 4 5 6 7 8 9
| <?php
class FooTest extends TestCase { public function testSomethingIsTrue() { $this->assertTrue(true); } }
|
数据验证
表单请求
Lumen 不支持表单请求。 如果想使用表单请求,则应使用完整的 Laravel 框架。
1 2 3 4 5 6 7 8 9 10
| use Illuminate\Http\Request;
$router->post('/user', function (Request $request) { $this->validate($request, [ 'name' => 'required', 'email' => 'required|email|unique:users' ]);
});
|
如果想要使用 exists
或 unique
验证规则,则应该在 bootstrap/app.php
文件中取消对 $app->withEloquent()
方法调用的注释。