blast / turbine
Requires
- php: >=5.5.9
- filp/whoops: ^2.1
- league/climate: ^3.2
- league/container: ^2.4
- league/event: ^2.1
- league/route: ~3.0
- monolog/monolog: ^1.22
- zendframework/zend-config: ^2.6
- zendframework/zend-diactoros: ^1.4
Requires (Dev)
- phpunit/phpunit: ~4.8
- symfony/http-kernel: ~2.7|~3.0
- symfony/psr-http-message-bridge: *
- zendframework/zend-stratigility: ~1.1.2|~1.3.1
Suggests
- symfony/http-kernel: Dependency of symfony http foundation adapter
- symfony/psr-http-message-bridge: Dependency of symfony http foundation adapter
- zendframework/zend-stratigility: Dependency of stratigility middleware adapter
Provides
README
Hawkbit\Application 微型框架是一个高度可定制的、事件驱动的框架,兼容 PSR-7、StackPHP 和 Zend Stratigility。
Hawkbit\Application 使用最新的 League\Route 进行路由,League\Container 进行依赖注入,League\Event 进行事件分发,Zend Config 进行配置。
Hawkbit\Application 是 Proton 的一个高级衍生产品,也是 Marco Bunge 的 Hawkbit 应用组件集合的一部分。Hawkbit 应用 1.x 也被称为 Blast Hawkbit 应用。
快速入门
请参阅 public/ 以获取示例用法并阅读文档。
集成
Hawkbit 应用也提供可选的包
- 数据库:
hawkbit/database
- Doctrine ORM 包装器:
hawkbit/doctrine
- Plates 视图引擎:
hawkbit/presentation
动机
我的愿景是提供一个能够以相同方式处理 HTTP 和 CLI 的微型框架。开发者应该能够重用它代码,根据他的需求设计业务层。Hawkbit 应该是一个辅助工具而不是预定义的框架。是的,它正在积极开发中。
我喜欢 PSR、phpleague 和最小化依赖集,并想创建一个使用最佳包的微型框架,并将其捆绑在美观的应用程序层中。我也喜欢基于组件的开发风格。
Hawkbit 是基于 phpleague 包构建的,并考虑了 PSR。Hawkbit 的设计是为了与您的代码共存而不是替换代码库。Hawkbit 的依赖性很小。最后但并非最不重要的是,Hawkbit 不强迫开发者如何设计应用程序的业务逻辑,因为我们更喜欢使用 POPO(Plain Old PHP Objects)作为控制器/命令(业务逻辑的访问器)。
目前我设计和开发所有的 Hawkbit 包,并管理整个代码库。我会很感激得到支持,甚至更好的是:贡献者!
有关详细信息,请参阅 Issue #33。
特别感谢
感谢提交 PR、识别 Bus 或任何其他改进!
安装
使用 Composer
Hawkbit\Application可在Packagist上找到,并可以使用Composer进行安装。可以通过运行以下命令或更新您的composer.json
文件来实现。
composer require hawkbit/hawkbit
composer.json
{ "require": { "hawkbit/hawkbit": "~2.0" } }
请确保将您的Composer自动加载文件也包含在您的项目中。
<?php require __DIR__ . '/vendor/autoload.php';
下载.zip文件
此项目也可以在GitHub上作为.zip
文件下载。访问发布页面,选择您想要的版本,然后点击“源代码(zip)”下载按钮。
要求
此版本支持以下版本的PHP。
- PHP 5.5
- PHP 5.6
- PHP 7.0
- PHP 7.1
- HHVM
设置
创建新应用
<?php require __DIR__.'/../vendor/autoload.php'; $app = new \Hawkbit\Application();
带配置创建新应用
<?php $config = [ 'key' => 'value' ]; $app = new \Hawkbit\Application($config);
添加路由
<?php /** @var Hawkbit\Application $app */ $app->get('/', function ($request, $response) { $response->getBody()->write('<h1>It works!</h1>'); return $response; }); $app->get('/hello/{name}', function ($request, $response, $args) { $response->getBody()->write( sprintf('<h1>Hello, %s!</h1>', $args['name']) ); return $response; });
运行应用
<?php $app->run();
参见我们的示例/public/index.php
。
配置
向应用添加额外配置
Hawkbit\Application配置由zend-config管理。
<?php //add many values $app->setConfig([ 'database' => [ 'default' => 'mysql://root:root@localhost/acmedb', ], 'services' => [ 'Acme\Services\ViewProvider', ] ]); //add a single value $app->setConfig('baseurl', 'localhost/'); $app->getConfig()->baseurl = 'localhost/'; $app->getConfig()['baseurl'] = 'localhost/';
访问配置
<?php //access all configuration $app->getConfig(); //get configuration item $default = $app->getConfig('database')->default; // returns 'mysql://root:root@localhost/acmedb $default = $app->getConfig()->database->default; // returns 'mysql://root:root@localhost/acmedb $default = $app->getConfig('database')['default']; // returns 'mysql://root:root@localhost/acmedb $default = $app->getConfig()['database']['default']; // returns 'mysql://root:root@localhost/acmedb
中间件
Hawkbit\Application中间件允许对生命周期执行进行高级控制。
<?php $app->addMiddleware(new Acme\SomeMiddleware);
Hawkbit\Application使用它自己的运行器Hawkbit\Application\MiddlewareRunner
路由
Hawkbit\Application使用league/route
的路由集成,并允许直接访问路由集合方法。
匿名函数的基本使用
<?php // index.php $app->get('/', function ($request, $response) { $response->getBody()->write('<h1>It works!</h1>'); return $response; }); $app->get('/hello/{name}', function ($request, $response, $args) { $response->getBody()->write( sprintf('<h1>Hello, %s!</h1>', $args['name']) ); return $response; }); $app->run();
从匿名函数访问应用
Hawkbit\Application允许通过闭包绑定从匿名函数访问自身。
<?php $app->get('/hello/{name}', function ($request, $response, $args) { // access Hawkbit\Application $app = $this; $response->getBody()->write( sprintf('<h1>Hello, %s!</h1>', $args['name']) ); return $response; });
控制器的基本使用
<?php require __DIR__.'/../vendor/autoload.php'; $app = new Hawkbit\Application(); $app->get('/', 'HomeController::index'); // calls index method on HomeController class $app->run();
<?php // HomeController.php use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; class HomeController { public function index(ServerRequestInterface $request, ResponseInterface $response, array $args) { $response->getBody()->write('<h1>It works!</h1>'); return $response; } }
控制器的自动构造函数注入
<?php // index.php require __DIR__.'/../vendor/autoload.php'; $app = new Hawkbit\Application(); $app->getContainer()->add('CustomService', new CustomService); $app->get('/', 'HomeController::index'); // calls index method on HomeController class $app->run();
请使用Service Providers中的boot方法以正确注入服务到控制器!
<?php // HomeController.php use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; class HomeController { /** * @var CustomService */ private $service; /** * @param CustomService $application */ public function __construct(CustomService $service = null) { $this->service = $service; } /** * @return CustomService */ public function getService() { return $this->service; } public function index(ServerRequestInterface $request, ResponseInterface $response, array $args) { //do somehing with service $service = $this->getService(); return $response; } }
有关路由的更多信息,请阅读此指南
路由组
Hawkbit\Application支持路由组。
<?php $app->group('/admin', function (\League\Route\RouteGroup $route) { //access app container (or any other method!) $app = $this; $route->map('GET', '/acme/route1', 'AcmeController::actionOne'); $route->map('GET', '/acme/route2', 'AcmeController::actionTwo'); $route->map('GET', '/acme/route3', 'AcmeController::actionThree'); });
可用变量
$route
-\League\Route\RouteGroup
$this
-\Hawkbit\Application
中间件集成
StackPHP
使用Stack\Builder
和Stack\Run
的基本使用
<?php // index.php require __DIR__.'/../vendor/autoload.php'; $app = new Hawkbit\Application(); $app->get('/', function ($request, $response) { $response->setContent('<h1>Hello World</h1>'); return $response; }); $httpKernel = new Hawkbit\Application\Symfony\HttpKernelAdapter($app); $stack = (new \Stack\Builder()) ->push('Some/MiddleWare') // This will execute first ->push('Some/MiddleWare') // This will execute second ->push('Some/MiddleWare'); // This will execute third $app = $stack->resolve($httpKernel); \Stack\run($httpKernel); // The app will run after all the middlewares have run
Zend Stratigility
使用Zend\Stratigility\MiddlewarePipe
的基本使用
<?php use Psr\Http\Message\ResponseInterface; use Zend\Diactoros\ServerRequestFactory; use Hawkbit\Application; use Hawkbit\Application\Stratigility\MiddlewarePipeAdapter; $application = new Application(); $application->get('/', function($request, ResponseInterface $response){ $response->getBody()->write('Hello World'); }); $middleware = new MiddlewarePipeAdapter($application); //wrap html heading $middleware->pipe('/', function($request, ResponseInterface $response, $next){ $response->getBody()->write('<h1>'); /** @var ResponseInterface $response */ $response = $next($request, $response); $response->getBody()->write('</h1>'); }); /** @var ResponseInterface $response */ $response = $middleware(ServerRequestFactory::fromGlobals(), $application->getResponse()); echo $response->getBody(); //prints <h1>Hello World</h1>
错误处理
Hawkbit\Application使用Whoops错误处理框架,并通过请求内容类型确定错误处理器。
设置自己的处理器
<?php $app->getErrorHandler()->push(new Acme\ErrorResponseHandler);
默认情况下,Hawkbit\Application以禁用错误选项的方式运行。要启用调试,请添加
<?php $app->setConfig('error', true);
默认情况下,Hawkbit\Application捕获所有错误。要禁用错误捕获,请添加
<?php $app->setConfig('error.catch', false);
控制台
控制台应用程序继承了Http应用程序的所有方法,除了路由和PSR-7处理以及捕获。除了Http应用程序,控制台应用程序不支持所有事件(更多信息请参考事件!)
日志记录
Hawkbit\Application内置了对Monolog的支持。要访问通道,请调用
<?php $app->getLogger('channel name');
有关通道的更多信息,请阅读此指南 - https://github.com/Seldaek/monolog/blob/master/doc/usage.md#leveraging-channels。
事件
您可以在生命周期中的七个点上拦截请求和响应。您可以通过Hawkbit\ApplicationEvent
来操作请求、响应和ErrorResponse。
应用程序事件
<?php /** @var \Hawkbit\Application\ApplicationEvent $event */ // custom params $event->getParamCollection(); // returns a mutable \ArrayObject // access application $event->getApplication();
request.received
<?php $app->addListener($app::EVENT_REQUEST_RECEIVED, function (\Hawkbit\Application\ApplicationEvent $event) { $request = $event->getRequest(); // manipulate $request $event->setRequest($request); });
当接收到请求但尚未由路由器处理时,会触发此事件。
response.created
对于控制台应用程序不可用!
<?php $app->addListener($app::EVENT_RESPONSE_CREATED, function (\Hawkbit\Application\ApplicationEvent $event) { $request = $event->getRequest(); $response = $event->getResponse(); // manipulate request or response $event->setRequest($request); $event->setResponse($response); });
在创建响应但尚未输出之前,会触发此事件。
response.sent
对于控制台应用程序不可用!请使用shutdown
<?php $app->addListener($app::EVENT_RESPONSE_SENT, function (\Hawkbit\Application\ApplicationEvent $event) { $request = $event->getRequest(); $response = $event->getResponse(); // manipulate request or response $event->setRequest($request); $event->setResponse($response); });
在输出响应并在应用程序生命周期完成后,会触发此事件。对于控制台应用程序不可用!
runtime.error
<?php $app->addListener($app::EVENT_RUNTIME_ERROR, function (\Hawkbit\Application\ApplicationEvent $event, $exception) use ($app) { //process exception });
当发生错误时,始终会触发此事件。
lifecycle.error
对于控制台应用程序不可用!请使用runtime.error
$errorResponse
用作默认响应
<?php $app->addListener($app::EVENT_LIFECYCLE_ERROR, function (\Hawkbit\Application\ApplicationEvent $event, \Exception $exception) { $errorResponse = $event->getErrorResponse(); //manipulate error response and process exception $event->setErrorResponse($errorResponse); });
当在处理请求/响应生命周期时发生错误时,会触发此事件。在runtime.error之后触发此事件
lifecycle.complete
对于控制台应用程序不可用!请使用shutdown
<?php $app->addListener($app::EVENT_LIFECYCLE_COMPLETE, function (\Hawkbit\Application\ApplicationEvent $event) { // access the request using $event->getRequest() // access the response using $event->getResponse() });
在输出响应并在应用程序生命周期完成后,会触发此事件。
shutdown
<?php $app->addListener($app::EVENT_SHUTDOWN, function (\Hawkbit\Application\ApplicationEvent $event, $response, $terminatedOutputBuffers = []) { // access the response using $event->getResponse() // access terminated output buffer contents // or force application exit() });
每次操作完成后或失败后,都会触发此事件。
自定义事件
您可以直接使用事件发射器触发自定义事件
<?php // addListener $app->addListener('custom.event', function ($event, $time) { return 'the time is '.$time; }); // or with class addListener $app->addListener(Acme\Event::class, function (Acme\Event $event, $time) { return 'the time is '.$time; }); // Publish $app->getEventEmitter()->emit('custom.event', time());
依赖注入容器
Hawkbit\Application使用League/Container
作为其依赖注入容器。
您可以使用ArrayAccess从主应用程序对象将单例对象绑定到容器中,或者通过容器访问
<?php /** @var Hawkbit\Application $app */ $app['db'] = function () use($app) { $config = $app->getConfig('database'); $manager = new Illuminate\Database\Capsule\Manager; $manager->addConnection([ 'driver' => 'mysql', 'host' => $config['host'], 'database' => $config['name'], 'username' => $config['user'], 'password' => $config['pass'], 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci' ], 'default'); $manager->setAsGlobal(); return $manager; };
或使用容器
<?php /** @var Hawkbit\Application $app */ $app->getContainer()->share('db', function () use($app) { $config = $app->getConfig('database'); $manager = new Illuminate\Database\Capsule\Manager; $manager->addConnection([ 'driver' => 'mysql', 'host' => $config['db_host'], 'database' => $config['db_name'], 'username' => $config['db_user'], 'password' => $config['db_pass'], 'charset' => 'utf8', 'collation' => 'utf8_unicode_ci' ], 'default'); $manager->setAsGlobal(); return $manager; });
可以使用容器的add
方法添加多重实例
<?php //callback $app->getContainer()->add('foo', function () { return new Foo(); });
可以使用Hawkbit\Application app上的register
方法或容器的addServiceProvider
注册服务提供程序
<?php $app->register('\My\Service\Provider'); $app->getContainer()->addServiceProvider('\My\Service\Provider');
有关服务提供程序的信息,请查看此页面 - http://container.thephpleague.com/service-providers/。
为了便于后续测试,建议您采用构造函数注入
<?php $app->getContainer()->add('Bar', function () { return new Bar(); }); $app->getContainer()->add('Foo', function () use ($app) { return new Foo( $app->getContainer()->get('Bar') ); });
容器
设置自己的容器需要\League\Container\ContainerInterface
的实例
<?php $app->setContainer($container);
获取容器
<?php $app->getContainer();
服务
Hawkbit\Application使用依赖注入容器来访问服务。以下集成可以交换。
配置器
用于 Application::setConfig()
、Application::getConfig()
和 Application::hasConfig()
<?php $app->getConfigurator();
<?php $app->getContainer()->share(\Zend\Config\Config::class, new \Zend\Config\Config([], true));
错误处理器
<?php $app->getContainer()->share(\Whoops\Run::class, new \Whoops\Run());
<?php $app->getErrorHandler();
错误响应处理器
<?php $app->getContainer()->share(\Whoops\Handler\HandlerInterface::class, Acme\ErrorResponseHandler::class);
<?php $app->getErrorResponseHandler();
psr 日志记录器
通过通道名称获取新的日志记录器实例
<?php $app->getContainer()->add(\Psr\Log\LoggerInterface::class, \Monolog\Logger::class);
<?php $app->getLogger('channel name');
获取可用的日志记录器通道列表
<?php $app->getLoggerChannels();
psr 服务器请求
<?php $app->getContainer()->share(\Psr\Http\Message\ServerRequestInterface::class, \Zend\Diactoros\ServerRequestFactory::fromGlobals());
<?php $app->getRequest();
psr 响应
<?php $app->getContainer()->add(\Psr\Http\Message\ResponseInterface::class, \Zend\Diactoros\Response::class);
<?php $app->getRequest();
响应发射器
<?php $app->getContainer()->share(\Zend\Diactoros\Response\EmitterInterface::class, \Zend\Diactoros\Response\SapiEmitter::class);
<?php $app->getResponseEmitter();
变更日志
有关最近更改的更多信息,请参阅 CHANGELOG。
测试
$ composer test
贡献
有关详细信息,请参阅 CONTRIBUTING。
安全
如果您发现任何与安全相关的问题,请通过电子邮件 [email protected] 而不是使用问题跟踪器。
鸣谢
- Marco Bunge
- Alex Bilbie (Proton)
- 所有贡献者
许可证
MIT 许可证 (MIT)。有关更多信息,请参阅 许可证文件。