1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Symfony ComponentAdvent Calendar 2022

Day 18

実行環境を切り替える、"Runtime"

Last updated at Posted at 2022-12-17

Symfony Component Advent Calendar 2022の18日目の記事です。

最初に

SymfonyはPHPのフレームワークのひとつです。しかし、公式サイトの説明文には

Symfony is a set of PHP Components, a Web Application framework, a Philosophy, and a Community — all working together in harmony.
(SymfonyはPHPコンポーネントのセットで、Webアプリケーションフレームワークで、哲学、そしてコミュニティです。それらがハーモニーを奏でながら動作しています。)

と書かれている通り、PHPコンポーネントのセットで、たくさんのコンポーネントを提供しており、それらを組み合わせてひとつのフレームワークとして動作しています。Symfonyのコンポーネントは、Symfony上だけで動作するのではなく、他のPHPフレームワークやアプリケーションでも動作している強力なものが揃っています。

今回はそれらの中から、役立ちそうなもの・お薦めしたいものを紹介していきたいと思います。

※記事内ではautoloadのインポートは省略します。

実行環境を切り替える、"Runtime"

Runtimは、アプリケーションの実行環境を簡単に切り替えることができるコンポーネントです。Symfony以外でも利用できます。

インストール

composer require symfony/runtime

何をするのか?

Runtimeはすごくざっくり言うと、**『リクエストを受けて、レスポンスを返す処理をまとめたもの』**を用意しています。この場合のリクエストは、CLIでの実行リクエストも含みます。例えばHTTP通信であれば、前回のHttpKernelの、リクエスト生成からレスポンス出力までのロジックが記述されたRuntimeがあり、そのRuntimeを実行することで、前述の処理が毎回実行できるようになります。

RoadRunnerをつかう

例えば、RoadrunnerでSymfonyを動かしたい場合、Runtimeを使わなければ以下のようなWorkerを作成します。

public/index.php

use Spiral\RoadRunner;
use Nyholm\Psr7;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;

include "vendor/autoload.php";

$psrFactory = new Psr7\Factory\Psr17Factory();
$worker = RoadRunner\Worker::create();
$worker = new RoadRunner\Http\PSR7Worker($worker, $psrFactory, $psrFactory, $psrFactory);

$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);

$httpFoundationFactory = new HttpFoundationFactory();
$httpMessageFactory = new PsrHttpFactory($psrFactory, $psrFactory, $psrFactory, $psrFactory);

while ($request = $worker->waitRequest()) {
    try {
        $sfRequest = $httpFoundationFactory->createRequest($request);
        $sfResponse = $kernel->handle($sfRequest);
        $worker->respond($httpMessageFactory->createResponse($sfResponse));

        if ($thiskernel instanceof TerminableInterface) {
            $this->terminate($sfRequest, $sfResponse);
        }
    } catch (\Throwable $e) {
        $worker->getWorker()->error((string) $e);
    }
}

Workerを用意したら、.rr.yamlに設定してrr serveを実行すれば終了です。

yaml/.rr.yaml
server:
  command: "php public/index.php"

http:
  address: 0.0.0.0:8080
  pool:
    num_workers: 4

Runtimeを利用する

もちろん上記でも動作しますが、Symfonyが用意したpublic/index.phpに手を入れる必要があります。つまり、Symfonyがアップグレードした際に、public/index.phpに対して変更があった場合、その変更に合わせて再度手を入れる必要があります。

そこで、Runtimeを使うわけですが、上記のRoadrunnerように変更した箇所をRuntimeに入れることで、public/index.phpを修正することなく、Roadrunnerで実行できるようになります。

RuntimeRunnerRuntimeを実装します。
Runnerには上記のRoadrunner用に変更した処理を、Runtimeは、前述のRunnerを条件に合えば返します。

Runner

まずはRunnerですが、上記の処理をほぼそのまま移します。オートワイヤリングできるので、オートワイヤリング可能なものは__construct()でDIします。

src/Runtime/RoadrunnerRunner.php


namespace App\Runtime;

use Nyholm\Psr7;
use Spiral\RoadRunner;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory;
use Symfony\Bridge\PsrHttpMessage\HttpFoundationFactoryInterface;
use Symfony\Bridge\PsrHttpMessage\HttpMessageFactoryInterface;
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\HttpKernel\TerminableInterface;
use Symfony\Component\Runtime\RunnerInterface;

class RoadrunnerRunner implements RunnerInterface
{
    private $kernel;
    private $httpFoundationFactory;
    private $httpMessageFactory;
    private $psrFactory;

    public function __construct(HttpKernelInterface $kernel, ?HttpFoundationFactoryInterface $httpFoundationFactory = null, ?HttpMessageFactoryInterface $httpMessageFactory = null)
    {
        $this->kernel = $kernel;
        $this->psrFactory = new Psr7\Factory\Psr17Factory();
        $this->httpFoundationFactory = $httpFoundationFactory ?? new HttpFoundationFactory();
        $this->httpMessageFactory = $httpMessageFactory ?? new PsrHttpFactory($this->psrFactory, $this->psrFactory, $this->psrFactory, $this->psrFactory);
    }

    public function run(): int
    {
        $worker = RoadRunner\Worker::create();
        $worker = new RoadRunner\Http\PSR7Worker($worker, $this->psrFactory, $this->psrFactory, $this->psrFactory);

        while ($request = $worker->waitRequest()) {
            try {
                $sfRequest = $this->httpFoundationFactory->createRequest($request);
                $sfResponse = $this->kernel->handle($sfRequest);
                $worker->respond($this->httpMessageFactory->createResponse($sfResponse));

                if ($this->kernel instanceof TerminableInterface) {
                    $this->kernel->terminate($sfRequest, $sfResponse);
                }
            } catch (\Throwable $e) {
                $worker->getWorker()->error((string) $e);
            }
        }

        return 0;
    }
}
Runtime

Runtime側はGenericRunnerの子クラスを作ります。

src/Runtime/RoadrunnerRuntime.php

namespace App\Runtime;

use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Runtime\GenericRuntime;
use Symfony\Component\Runtime\RunnerInterface;

class RoadrunnerRuntime extends GenericRuntime
{
    public function getRunner(?object $application): RunnerInterface
    {
        if ($application instanceof HttpKernelInterface) {
            return new RoadrunnerRunner($application);
        }

        // HttpKernelではなかった場合は、デフォルトのRunnerを実行する
        return parent::getRunner($application);
    }
}
設定

上記で作成したRuntimeを使うように設定します。なんと.envに書くだけで設定できます。

.env
APP_RUNTIME=App\Runtime\RoadrunnerRuntime

あとは、rr serveすれば動作します。

用意されているサードパーティRuntime

実は、すでにいくつかのRuntimeは用意されています。

現在、

  • Swoole
  • Roadrunner
  • React PHP
  • Bref
  • Google Cloud

が公開されており、これらを使えば簡単に実行環境を切り替えることができます。

まとめ

今回は、Runtimeを紹介しました。現在のSymfonyではこのRuntimeを使うようにpublic/index.phpが記述されています。
どのようになってるか調べた記事があるので、こちらもぜひご覧ください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?