使用MicroKernelTrait创建你自己的微框架
一个传统的Symfony程序包括一个合理的目录结构,各种配置文件以及一个注册了若干bundle的AppKernel
文件。这就是一个可以投入使用的全功能程序。
但是你知道吗,你能够仅仅在一个文件中创建这样一个全功能的Symfony程序?多亏了MicroKernelTrait
,这完全是可能的。它允许你从一个微小程序起步,然后随你所需地添加功能和结构。
单文件Symfony程序 ¶
从一个空目录开始。使用Composer来获取依赖,即symofny/symfony
:
1 |
$ composer require symfony/symfony |
接下来创建index.php
文件,用它来创建kernel类并执行之:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\Httpfoundation\jsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
// require Composer's autoloader / 把Composer的自动加载器包容进来
require __DIR__.'/vendor/autoload.php';
class AppKernel extends Kernel
{
use MicroKernelTrait;
public function registerBundles()
{
return array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle()
);
}
protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
{
// PHP equivalent of config.yml / 相当于config.yml的PHP
$c->loadFromExtension('framework', array(
'secret' => 'S0ME_SECRET'
));
}
protected function configureRoutes(RouteCollectionBuilder $routes)
{
// kernel is a service that points to this class
// optional 3rd argument is the route name
// kernel是一个指向这个类的服务。可选的第三个参数是路由名称
$routes->add('/ranDOM/{limit}', 'kernel:randomAction');
}
public function randomAction($limit)
{
return new JsonResponse(array(
'number' => rand(0, $limit)
));
}
}
$kernel = new AppKernel('dev', true);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response); |
这就可以了!要测试它,你可以直接启动PHP内置的web server:
1 |
$ php -S localhost:8000 |
> http://localhost:8000/random/10
然后在浏览器中查看JSON响应:
“微”内核中的方法 ¶
当你使用MicroKernelTrait
时,你的kernel需要明确具备三个方法,它们分别定义了你的bundles、services和routes:
- registerBundles()
- 与你在普通kernel中见到的
registerBundles()
相同。 - configureContainer(ContainerBuilder $c, LoaderInterface $loader)
- 这个方法构建和配置了容器。实践中,你要使用
loadFromExtension
来配置不同的bundles(这相当于[配置]你在普通的config.yml
文件中所见到的[那些配置信息])。你也可以在PHP中直接注册服务,或是加载外部配置文件(下例有展示)。 - configureRoutes(RouteCollectionBuilder $routes)
- 在这个方法中你要对程序添加路由。
RouteCollectionBuilder
有一些方法可以让PHP在添加路由时充满乐趣。你也可以加载外部路由文件(下例有展示)。
高级示例:Twig、Annotations以及Web除错工具条 ¶
MicroKernelTrait
的目的不是 要去搞一个单文件程序。而是要给你一个“选择你自己的bundle和结构”的超能力。
首先,你可能希望把你的PHP类放到src/
目录下。配置你的composer.json
文件来从那里加载它:
1 2 3 4 5 6 7 8 9 10 |
{
"require": {
"...": "..."
},
"autoload": {
"psr-4": {
"": "src/"
}
}
} |
现在,假设你希望使用Twig,并且通过annotations来加载路由。对于annotation方式的路由,你需要SensioFrameworkExtraBundle。它已内置在Symfony标准版的项目之中。但在本例,你还是需要下载它:
1 |
$ composer require sensio/framework-extra-bundle |
不要把所有东西 都放到index.php
中,创建一个新的app/AppKernel.php
以容纳kernel。现在它看起来像:
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
// app/AppKernel.php
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
use Doctrine\Common\Annotations\AnnotationRegistry;
// require Composer's autoloader
$loader = require __DIR__.'/../vendor/autoload.php';
// auto-load annotations
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
class AppKernel extends Kernel
{
use MicroKernelTrait;
public function registerBundles()
{
$bundles = array(
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle()
);
if ($this->getEnvironment() == 'dev') {
$bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
}
return $bundles;
}
protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
{
$loader->load(__DIR__.'/config/config.yml');
// configure WebProfilerBundle only if the bundle is enabled
// 配置WebProfilerBundle,只当此bundle被开启时
if (isset($this->bundles['WebProfilerBundle'])) {
$c->loadFromExtension('web_profiler', array(
'toolbar' => true,
'intercept_redirects' => false,
));
}
}
protected function configureRoutes(RouteCollectionBuilder $routes)
{
// import the WebProfilerRoutes, only if the bundle is enabled
// 导入WebProfilerRoutes,只当此bundle被开启时
if (isset($this->bundles['WebProfilerBundle'])) {
$routes->import('@WebProfilerBundle/Resources/config/routing/wdt.xml', '/_wdt');
$routes->import('@WebProfilerBundle/Resources/config/routing/profiler.xml', '/_profiler');
}
// load the annotation routes / 加载annotation路由
$routes->import(__DIR__.'/../src/App/Controller/', '/', 'annotation');
}
// optional, to use the standard Symfony cache directory
// 可选,使用标准的Symfony缓存目录
public function getCacheDir()
{
return __DIR__.'/../var/cache/'.$this->getEnvironment();
}
// optional, to use the standard Symfony logs directory
// 可选,使用标准的Symfony日志目录
public function getLogDir()
{
return __DIR__.'/../var/logs';
}
} |
不像之前的kernel,这里加载了一个外部的app/config/config.yml
文件,因为配置信息开始变得巨大了:
1 2 3 4 5 6 |
# app/config/config.yml
framework:
secret: S0ME_SECRET
templating:
engines: ['twig']
profiler: { only_exceptions: false } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<!-- app/config/config.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:framework="http://symfony.com/schema/dic/symfony"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/symfony http://symfony.com/schema/dic/symfony/symfony-1.0.xsd">
<framework:config secret="S0ME_SECRET">
<framework:templating>
<framework:engine>twig</framework:engine>
</framework:templating>
<framework:profiler only-exceptions="false" />
</framework:config>
</container> |
1 2 3 4 5 6 7 8 9 10 |
// app/config/config.php
$container->loadFromExtension('framework', array(
'secret' => 'S0ME_SECRET',
'templating' => array(
'engines' => array('twig'),
),
'profiler' => array(
'only_exceptions' => false,
),
)); |
它也从src/App/Controller/
目录中加载annotation 路由,目录下面有一个文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
/ src/App/Controller/MicroController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class MicroController extends Controller
{
/**
* @Route("/random/{limit}")
*/
public function randomAction($limit)
{
$number = rand(0, $limit);
return $this->render('micro/random.HTML.twig', array(
'number' => $number
));
}
} |
模板文件应该放在Resources/views
目录下,无论你的kernel 放在哪里。 由于AppKernel
位于app/
下,那么模板就应该是app/Resources/views/micro/random.html.twig
。
最后,你需要一个前端控制器来启动和运行程序。创建一个web/index.php
:
1 2 3 4 5 6 7 8 9 10 11 |
// web/index.php
use Symfony\Component\HttpFoundation\Request;
require __DIR__.'/../app/AppKernel.php';
$kernel = new AppKernel('dev', true);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response); |
That's it! This /random/10
URL will work, Twig will render, and you'll even
get the web debug toolbar to show up at the bottom. The final structure looks like
this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
your-project/
├─ app/
| ├─ AppKernel.php
│ ├─ config/
│ └─ Resources
| └─ views
| ├─ base.html.twig
| └─ micro
| └─ random.html.twig
├─ src/
│ └─ App
| └─ Controller
| └─ MicroController.php
├─ var/
| ├─ cache/
│ └─ logs/
├─ vendor/
│ └─ ...
├─ web/
| └─ index.php
├─ composer.json
└─ composer.lock |
嘿,看起来很像传统的 Symfony程序呀!你是对的:MicroKernelTrait
就是 Symfony:但你却可以极为轻松地控制你自己的结构和功能。