microframework(微框架)在字面上指的是“简约的web程序框架”。开发者通常把这个词与“快速、小巧的框架”进行关联,例如Silex。但你也可以理解为简单、不强制的框架,你可以选择性地进入与架构有关的关键部分。

得益于弹性的内部架构,从第一天开始我们就可以把Symfony作为微框架来使用。不过,只有少数开发者以这种方式使用Symfony,因为它还不能完全保证便利性。

Symfony 2.8引入了一个全新的microkernel trait,来简化创建单文件(或仅仅更小)的Symfony程序。一个“Hello World”程序,若使用Symfony作为微框架的话,看上去像这样:

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
// app/MicroKernel.php
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
 
class MicroKernel extends Kernel
{
    use MicroKernelTrait;
 
    public function registerBundles()
    {
        return array(new Symfony\Bundle\FrameworkBundle\FrameworkBundle());
    }
 
    protected function configureRoutes(RouteCollectionBuilder $routes)
    {
        $routes->add('/', 'kernel:indexAction', 'index');
    }
 
    protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
    {
        $c->loadFromExtension('framework', ['secret' => '12345']);
    }
 
    public function indexAction()
    {
        return new Response('Hello World');
    }
}

单一的MicroKernel类即可开启bundles并配置它们,定义路由,甚至控制controller代码。如果抛开强制使用的use导入以及function声明等关键字,上面例程中的实际PHP代码只有4行。反复地观想一下:一个全功能的Symfony程序只有四行代码!

这个新的microkernel,并未改善Symfony原始性能,因为它只是改变了路由和bundle的“注册方式”。不过,由于你只是开启了你要用到的功能,Symfony标准版的大量bundles都被禁用了。这就能够解释程序的性能在理解上的不同:

Symfony微框架的最强大之处在于,你正踩在Symfony的肩膀上构建你的程序,意味着你毋须面对寻常微框架所导致的各种束缚。所有不可思议的Symfony功能连同bundles,皆可在你的程序成长过程中“你所需要”的时候,立即投入使用。

下面的例程是,Hello World程序被扩展为支持Twig模板、Web除错工具条以及Profiler分析器。这一切仍然可以单文件输出:

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
// app/MicroKernel.php
 
class MicroKernel extends Kernel
{
    use MicroKernelTrait;
 
    public function registerBundles()
    {
        $bundles = array(
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
        );
 
        if (in_array($this->getEnvironment(), array('dev', 'test'), true)) {
            $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
        }
 
        return $bundles;
    }
 
    protected function configureRoutes(RouteCollectionBuilder $routes)
    {
        $routes->mount('/_wdt', $routes->import('@WebProfilerBundle/Resources/config/routing/wdt.xml'));
        $routes->mount('/_profiler', $routes->import('@WebProfilerBundle/Resources/config/routing/profiler.xml'));
 
        $routes->add('/', 'kernel:indexAction', 'index');
    }
 
    protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
    {
        // load bundles' configuration
        $c->loadFromExtension('framework', [
            'secret' => '12345',
            'profiler' => null,
            'templating' => ['engines' => ['twig']],
        ]);
 
        $c->loadFromExtension('web_profiler', ['toolbar' => true]);
 
        // add configuration parameters
        $c->setParameter('mail_sender', 'user@example.com');
 
        // register services
        $c->register('app.markdown', 'AppBundle\\Service\\Parser\\Markdown');
    }
 
    public function indexAction()
    {
        return $this->container->get('templating')->renderResponse('index.html.twig');
    }
}

令程序“单文件化”绝非microkernel的本意。真实的使用场景是创建小型化的Symfony程序,也许它只在每个环境中使用了一个services.yml文件和一个config.yml文件,而路由在常规控制器类中以annotation方式被定义。以下是实现此需求时的完整代码示例:

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
// app/MicroKernel.php
 
// ...
 
class MicroKernel extends Kernel
{
    use MicroKernelTrait;
 
    public function registerBundles()
    {
        return array(
            new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
            new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
            new Symfony\Bundle\TwigBundle\TwigBundle(),
            new AppBundle\AppBundle(),
        );
    }
 
    protected function configureRoutes(RouteCollectionBuilder $routes)
    {
        $routes->mount('/', $routes->import('@AppBundle/Controller', 'annotation'));
    }
 
    protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
    {
        $loader->load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml');
        $loader->load(__DIR__.'/config/services.yml');
    }
}

在使用microkernel来替代AppKernel时,一定不要忘记更新前端控制器:

1
2
3
4
5
6
7
8
9
10
// web/app.php
use Symfony\Component\HttpFoundation\Request;
 
$loader = require __DIR__.'/../app/autoload.php';
require_once __DIR__.'/../app/MicroKernel.php';
 
$app = new MicroKernel('prod', false);
$app->loadClassCache();
 
$app->handle(Request::createFromGlobals())->send();

这样一来,如果你正在微框架和全功能框架之间进行选择的话,你将有一个全新的可能性。现在你可以在同一程序中同时拥有二者,并且毋须在功能上进行任何妥协。