Enrico刚从phpDay回来,他在会上谈到了Expressive和即将推出的ZendFramework3。他带回的一条反馈与人们如何看待他们应该构建Expressive应用程序有关:许多人认为,根据我们的例子,它是完全配置驱动!
事实证明,这与事实相去甚远;我们开发了我们的API来模仿传统微框架的API,然后在其之上构建了一个配置层以允许进行替换。然而,混合搭配这两种想法不仅可行,而且非常有趣!
作为实验,我拿了自己网站的源代码,做了一些调整:
- 我将中间件管道从我的
config/autoload/middleware-pipeline.global.php文件导入到我的public/index.php中的programmaticdeclarations。 - 我将路由中间件定义从我的
config/autoload/routes.global.php文件导入到我的public/index.php中的编程声明中。
要记住的点点滴滴:
- 使用完全限定的类名来引用您的中间件,就像您在配置中所做的那样。这允许Expressive从您仍在配置的容器中提取它们!
- 在定义管道和定义路由时,操作顺序很重要。然而,管道和路由可以单独定义,我建议这样做;这样您就可以独立于路由定义查看整个应用程序管道。
这是我最终得到的结果。
首先,我的中间件管道配置只变成依赖项列表,以确保服务正确连接:
// config/autoload/middleware-pipeline.php
use Mwop\Auth\Middleware as AuthMiddleware;
use Mwop\Auth\MiddlewareFactory as AuthMiddlewareFactory;
use Mwop\Factory\Unauthorized as UnauthorizedFactory;
use Mwop\Redirects;
use Mwop\Unauthorized;
use Mwop\XClacksOverhead;
return [
'dependencies' => [
'invokables' => [
Redirects::class => Redirects::class,
XClacksOverhead::class => XClacksOverhead::class,
],
'factories' => [
AuthMiddleware::class => AuthMiddlewareFactory::class,
Helper\UrlHelperMiddleware::class => Helper\UrlHelperMiddlewareFactory::class,
Unauthorized::class => UnauthorizedFactory::class,
],
],
];
同样,路由配置现在也只是服务配置:
// config/autoload/routes.global.php
use Mwop\Blog;
use Mwop\ComicsPage;
use Mwop\Contact;
use Mwop\Factory;
use Mwop\HomePage;
use Mwop\Job;
use Mwop\ResumePage;
use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;
use Zend\Expressive\Router\RouterInterface;
use Zend\Expressive\Router\FastRouteRouter;
return [
'dependencies' => [
'delegators' => [
Blog\DisplayPostMiddleware::class => [
Blog\CachingDelegatorFactory::class,
],
],
'invokables' => [
Blog\FeedMiddleware::class => Blog\FeedMiddleware::class,
Blog\Console\SeedBlogDatabase::class => Blog\Console\SeedBlogDatabase::class,
BodyParamsMiddleware::class => BodyParamsMiddleware::class,
RouterInterface::class => FastRouteRouter::class,
],
'factories' => [
Blog\DisplayPostMiddleware::class => Blog\DisplayPostMiddlewareFactory::class,
Blog\ListPostsMiddleware::class => Blog\ListPostsMiddlewareFactory::class,
Contact\LandingPage::class => Contact\LandingPageFactory::class,
Contact\Process::class => Contact\ProcessFactory::class,
Contact\ThankYouPage::class => Contact\ThankYouPageFactory::class,
ComicsPage::class => Factory\ComicsPage::class,
HomePage::class => Factory\PageFactory::class,
Job\GithubFeed::class => Job\GithubFeedFactory::class,
ResumePage::class => Factory\PageFactory::class,
'Mwop\OfflinePage' => Factory\PageFactory::class,
],
],
];
最后,让我们看一下public/index.php。如前所述,Expressive定义了与其他微框架类似的API。这意味着您可以调用诸如$app->get()、$app->post()等带有路由、要执行的中间件,并且在在Expressive的情况下,路由名称(用于应用程序中的URI生成)。这是完成后的样子:
// public/index.php
namespace Mwop;
use Zend\Expressive\Application;
use Zend\Expressive\Helper;
// Delegate static file requests back to the PHP built-in webserver
if (php_sapi_name() === 'cli-server'
&& is_file(__DIR__ . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))
) {
return false;
}
chdir(dirname(__DIR__));
require_once 'vendor/autoload.php';
$container = require 'config/container.php';
$app = $container->get(Application::class);
// Piped middleware
$app->pipe(XClacksOverhead::class);
$app->pipe(Redirects::class);
$app->pipe('/auth', Auth\Middleware::class);
$app->pipeRoutingMiddleware();
$app->pipe(Helper\UrlHelperMiddleware::class);
$app->pipeDispatchMiddleware();
$app->pipe(Unauthorized::class);
// Routed middleware
// General pages
$app->get('/', HomePage::class, 'home');
$app->get('/comics', ComicsPage::class, 'comics');
$app->get('/offline', OfflinePage::class, 'offline');
$app->get('/resume', ResumePage::class, 'resume');
// Blog
$app->get('/blog[/]', Blog\ListPostsMiddleware::class, 'blog');
$app->get('/blog/{id:[^/]+}.html', Blog\DisplayPostMiddleware::class, 'blog.post');
$app->get('/blog/tag/{tag:php}.xml', Blog\FeedMiddleware::class, 'blog.feed.php');
$app->get('/blog/{tag:php}.xml', Blog\FeedMiddleware::class, 'blog.feed.php.also');
$app->get('/blog/tag/{tag:[^/]+}/{type:atom|rss}.xml', Blog\FeedMiddleware::class, 'blog.tag.feed');
$app->get('/blog/tag/{tag:[^/]+}', Blog\ListPostsMiddleware::class, 'blog.tag');
$app->get('/blog/{type:atom|rss}.xml', Blog\FeedMiddleware::class, 'blog.feed');
// Contact form
$app->get('/contact[/]', Contact\LandingPage::class, 'contact');
$app->post('/contact/process', Contact\Process::class, 'contact.process');
$app->get('/contact/thank-you', Contact\ThankYouPage::class, 'contact.thank-your');
// Zend Server jobs
$app->post('/jobs/clear-cache', Job\ClearCache::class, 'job.clear-cache');
$app->post('/jobs/comics', Job\Comics::class, 'job.comics');
$app->post('/jobs/github-feed', Job\GithubFeed::class, 'job.github-feed');
$app->run();
这种方法在定义中间件内联之间提供了一个很好的中间地带:
$app->get('/', function ($request, $response, $next) {
// ...
}, 'home');
和直接配置方法:
'routes' => [
[
'path' => '/',
'middleware' => HomePage::class,
'allowed_methods' => ['GET'],
'name' => 'home',
],
/ * ... */
然而,它失去了一些灵活性:通过配置驱动的方法,我们可以轻松地定义一些只在开发中执行的路由或管道中间件,并确保它们发生的顺序——这是编程方法不容易做到的。
然而,本练习的主要目的是证明Expressive允许您选择自己的方法,这是该项目背后的指导原则。
