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允许您选择自己的方法,这是该项目背后的指导原则。