How To Create Symfony Applications with Multiple Kernels

Caution

注意

Creating applications with multiple kernels is no longer recommended by Symfony. Consider creating multiple small applications instead.

複数のカーネルでアプリケーションを作成することは、Symfony では推奨されなくなりました。代わりに、複数の小さなアプリケーションを作成することを検討してください。

In most Symfony applications, incoming requests are processed by the public/index.php front controller, which instantiates the src/Kernel.php class to create the application kernel that loads the bundles and handles the request to generate the response.

ほとんどの Symfony アプリケーションでは、受信リクエストは public/index.php フロント コントローラーによって処理されます。これは、src/Kernel.php クラスをインスタンス化して、バンドルをロードし、リクエストを処理してレスポンスを生成するアプリケーション カーネルを作成します。

This single kernel approach is a convenient default, but Symfony applications can define any number of kernels. Whereas environments run the same application with different configurations, kernels can run different parts of the same application.

この単一カーネル アプローチは便利なデフォルトですが、Symfony アプリケーションは任意の数のカーネルを定義できます。環境が同じアプリケーションを異なる構成で実行するのに対し、カーネルは同じアプリケーションの異なる部分を実行できます。

These are some of the common use cases for creating multiple kernels:

以下は、複数のカーネルを作成する一般的なユース ケースの一部です。
  • An application that defines an API could define two kernels for performance reasons. The first kernel would serve the regular application and the second one would only respond to the API requests, loading less bundles and enabling less features;
    API を定義するアプリケーションは、パフォーマンス上の理由から 2 つのカーネルを定義できます。最初のカーネルは通常のアプリケーションを提供し、2 番目のカーネルは API リクエストにのみ応答し、ロードするバンドルを減らし、機能を有効にしません。
  • A highly sensitive application could define two kernels. The first one would only load the routes that match the parts of the application exposed publicly. The second kernel would load the rest of the application and its access would be protected by the web server;
    機密性の高いアプリケーションでは、2 つのカーネルを定義できます。最初のカーネルは、公開されているアプリケーションの部分に一致するルートのみをロードします。2 番目のカーネルは、アプリケーションの残りの部分をロードし、そのアクセスは Web サーバーによって保護されます。
  • A micro-services oriented application could define several kernels to enable/disable services selectively turning a traditional monolith application into several micro-applications.
    マイクロサービス指向のアプリケーションは、複数のカーネルを定義してサービスを選択的に有効/無効にし、従来のモノリス アプリケーションを複数のマイクロ アプリケーションに変えることができます。

Adding a new Kernel to the Application

Creating a new kernel in a Symfony application is a three-step process:

Symfony アプリケーションで新しいカーネルを作成するには、次の 3 ステップのプロセスがあります。
  1. Create a new front controller to load the new kernel;
    新しいカーネルをロードする新しいフロント コントローラーを作成します。
  2. Create the new kernel class;
    新しいカーネル クラスを作成します。
  3. Define the configuration loaded by the new kernel.
    新しいカーネルによってロードされる構成を定義します。

The following example shows how to create a new kernel for the API of a given Symfony application.

次の例は、特定の Symfony アプリケーションの API 用に新しいカーネルを作成する方法を示しています。

Step 1) Create a new Front Controller

Instead of creating the new front controller from scratch, it's easier to duplicate the existing one. For example, create public/api.php from public/index.php.

新しいフロント コントローラーをゼロから作成する代わりに、既存のものを複製する方が簡単です。たとえば、public/index.php から public/api.php を作成します。

Then, update the code of the new front controller to instantiate the new kernel class instead of the usual Kernel class:

次に、新しいフロント コントローラーのコードを更新して、通常の Kernel クラスの代わりに新しい kernelclass をインスタンス化します。
1
2
3
4
5
6
7
// public/api.php
// ...
$kernel = new ApiKernel(
    $_SERVER['APP_ENV'] ?? 'dev',
    $_SERVER['APP_DEBUG'] ?? ('prod' !== ($_SERVER['APP_ENV'] ?? 'dev'))
);
// ...

Tip

ヒント

Another approach is to keep the existing index.php front controller, but add an if statement to load the different kernel based on the URL (e.g. if the URL starts with /api, use the ApiKernel).

もう 1 つの方法は、既存の index.php フロント コントローラーを維持することですが、if ステートメントを追加して、URL に基づいて別のカーネルをロードします (たとえば、URL が /api で始まる場合は、ApiKernel を使用します)。

Step 2) Create the new Kernel Class

Now you need to define the ApiKernel class used by the new front controller. The easiest way to do this is by duplicating the existing src/Kernel.php file and make the needed changes.

ここで、新しいフロント コントローラーで使用される ApiKernel クラスを定義する必要があります。これを行う最も簡単な方法は、既存の src/Kernel.php ファイルを複製し、必要な変更を加えることです。

In this example, the ApiKernel will load fewer bundles than the default Kernel. Be sure to also change the location of the cache, logs and configuration files so they don't collide with the files from src/Kernel.php:

この例では、ApiKernel は defaultKernel よりも少ないバンドルをロードします。 src/Kernel.php のファイルと衝突しないように、キャッシュ、ログ、および構成ファイルの場所も必ず変更してください。
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
// src/ApiKernel.php
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;

class ApiKernel extends Kernel
{
    use MicroKernelTrait;

    public function getProjectDir(): string
    {
        return \dirname(__DIR__);
    }

    public function getCacheDir(): string
    {
        return $this->getProjectDir().'/var/cache/api/'.$this->environment;
    }

    public function getLogDir(): string
    {
        return $this->getProjectDir().'/var/log/api';
    }

    protected function configureContainer(ContainerConfigurator $container): void
    {
        $container->import('../config/api/{packages}/*.yaml');
        $container->import('../config/api/{packages}/'.$this->environment.'/*.yaml');

        if (is_file(\dirname(__DIR__).'/config/api/services.yaml')) {
            $container->import('../config/api/services.yaml');
            $container->import('../config/api/{services}_'.$this->environment.'.yaml');
        } else {
            $container->import('../config/api/{services}.php');
        }
    }

    protected function configureRoutes(RoutingConfigurator $routes): void
    {
        $routes->import('../config/api/{routes}/'.$this->environment.'/*.yaml');
        $routes->import('../config/api/{routes}/*.yaml');
        // ... load only the config routes strictly needed for the API
    }

    // If you need to run some logic to decide which bundles to load,
    // you might prefer to use the registerBundles() method instead
    private function getBundlesPath(): string
    {
        // load only the bundles strictly needed for the API
        return $this->getProjectDir().'/config/api_bundles.php';
    }
}

Step 3) Define the Kernel Configuration

Finally, define the configuration files that the new ApiKernel will load. According to the above code, this config will live in one or multiple files stored in config/api/ and config/api/ENVIRONMENT_NAME/ directories.

最後に、新しい ApiKernel がロードする構成ファイルを定義します。上記のコードによると、この構成は config/api/ および config/api/ENVIRONMENT_NAME/ ディレクトリに格納された 1 つまたは複数のファイルに存在します。

The new configuration files can be created from scratch when you load only a few bundles, because it will be small. Otherwise, duplicate the existing config files in config/packages/ or better, import them and override the needed options.

少数のバンドルのみをロードする場合は、小さいため、新しい構成ファイルをゼロから作成できます。それ以外の場合は、config/packages/ またはそれ以上の既存の構成ファイルを複製し、それらをインポートして、必要なオプションを上書きします。

Executing Commands with a Different Kernel

The bin/console script used to run Symfony commands always uses the default Kernel class to build the application and load the commands. If you need to run console commands using the new kernel, duplicate the bin/console script and rename it (e.g. bin/api).

Symfony コマンドの実行に使用される bin/console スクリプトは、常に defaultKernel クラスを使用してアプリケーションをビルドし、コマンドをロードします。新しいカーネルを使用してコンソール コマンドを実行する必要がある場合は、bin/consolescript を複製し、名前を変更します (例: bin/api)。

Then, replace the Kernel instance by your own kernel instance (e.g. ApiKernel). Now you can run commands using the new kernel (e.g. php bin/api cache:clear).

次に、カーネル インスタンスを独自のカーネル インスタンス (ApiKernel など) に置き換えます。新しいカーネルを使用してコマンドを実行できるようになりました (例: php bin/api cache:clear)。

Note

ノート

The commands available for each console script (e.g. bin/console and bin/api) can differ because they depend on the bundles enabled for each kernel, which could be different.

各コンソール スクリプト (例: bin/console および bin/api) で使用できるコマンドは、各カーネルで有効になっているバンドルに依存するため、異なる可能性があります。

Rendering Templates Defined in a Different Kernel

If you follow the Symfony Best Practices, the templates of the default kernel will be stored in templates/. Trying to render those templates in a different kernel will result in a There are no registered paths for namespace "__main__" error.

Symfony のベスト プラクティスに従えば、デフォルトのカーネルのテンプレートは templates/ に保存されます。これらのテンプレートを別のカーネルでレンダリングしようとすると、名前空間 "__main__" の登録済みパスがありませんというエラーが発生します。

In order to solve this issue, add the following configuration to your kernel:

この問題を解決するには、次の構成をカーネルに追加します。
1
2
3
4
5
# config/api/twig.yaml
twig:
    paths:
        # allows to use api/templates/ dir in the ApiKernel
        "%kernel.project_dir%/api/templates": ~

Running Tests Using a Different Kernel

In Symfony applications, functional tests extend by default from the WebTestCase class. Inside that class, a method called getKernelClass() tries to find the class of the kernel to use to run the application during tests. The logic of this method does not support multiple kernel applications, so your tests won't use the right kernel.

Symfony アプリケーションでは、機能テストはデフォルトで WebTestCase クラスから拡張されます。そのクラス内で、getKernelClass() と呼ばれるメソッドが、テスト中にアプリケーションを実行するために使用するカーネルのクラスを見つけようとします。このメソッドのロジックは複数のカーネル アプリケーションをサポートしていないため、テストで適切なカーネルが使用されません。

The solution is to create a custom base class for functional tests extending from WebTestCase class and overriding the getKernelClass() method to return the fully qualified class name of the kernel to use:

解決策は、機能テスト用のカスタム基本クラスを作成して WebTestCase クラスから拡張し、使用するカーネルの完全修飾クラス名を返すように getKernelClass() メソッドをオーバーライドすることです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

// tests needing the ApiKernel to work, now must extend this
// ApiTestCase class instead of the default WebTestCase class
class ApiTestCase extends WebTestCase
{
    protected static function getKernelClass()
    {
        return 'App\ApiKernel';
    }

    // this is needed because the KernelTestCase class keeps a reference to
    // the previously created kernel in its static $kernel property. Thus,
    // if your functional tests do not run in isolated processes, a later run
    // test for a different kernel will reuse the previously created instance,
    // which points to a different kernel
    protected function tearDown()
    {
        parent::tearDown();

        static::$class = null;
    }
}

Adding more Kernels to the Application

If your application is very complex and you create several kernels, it's better to store them in their own directories instead of messing with lots of files in the default src/ directory:

アプリケーションが非常に複雑で、複数のカーネルを作成する場合は、デフォルトの src/ ディレクトリで多くのファイルをいじるよりも、カーネルを独自のディレクトリに格納することをお勧めします。
1
2
3
4
5
6
7
8
9
10
11
12
project/
├─ src/
│  ├─ ...
│  └─ Kernel.php
├─ api/
│  ├─ ...
│  └─ ApiKernel.php
├─ ...
└─ public/
    ├─ ...
    ├─ api.php
    └─ index.php