レガシーPHPシステムからSymfonyへ移行
ベストプラクティス
E2Eテスト
Symfony Pantherなどのエンドツーエンドのテストを目的としたツール
apacheで、既存PHPファイルへのリクエストを、Symfonyのフロントエンドポイントへ書き換える
Front Controller with Legacy Bridge,
グローバルスコープ
Legacy Route Loader
Symfony内部のスコープ
ルートローダーを追加、カスタムできる
特定パスのルートローダーを定義するなど
Kernel
Kernelは、HttpKernelを継承している
Front Controller with Legacy Bridgeは、基本Kernelの作法
セッション
http-foundationのコンポーネントが必要
composer require symfony/http-foundation
レガシーセッションと統合する場合は、handler_id ~にする。
handler_id にIDを指定してオーバーライドすると、レガシーセッションのセッションが使用できない?
オーバーライドするには、レガシーセッションが開始される前に、Symfonyが初期化されている必要がある。
code:yml
# config/packages/framework.yaml
framework:
session:
storage_factory_id: session.storage.factory.php_bridge
handler_id: ~
リファレンス
参考
Config
Configuring Environment Variables in Production
composer dump-env prod
.envが順番に読み込まれるため、高速化のため、環境変数をダンプし.env.local.phpを作成しておく。
Proxyがあるときの設定
How to Configure Symfony to Work behind a Load Balancer or a Reverse Proxy
Log
deprecationのaction_levelはdebug
リクエストのログを取るために、fingers_crossedを指定しようとしたが、channelsをdeprecationにするとdeprecationのみのフィルターになってしまう
How to Add extra Data to Log Messages via a Processor
code:yaml
# config/services.yaml
services:
App\Logger\SessionRequestProcessor:
tags:
- { name: monolog.processor }
テスト
You can add multiple #AsEventListener attributes to configure different methods. The method property is optional, and when not defined, it defaults to on + uppercased event name. In the example below, the 'foo' event listener doesn't explicitly define its method, so the onFoo() method will be called: Event Listeners
7) The kernel.response Event
$routeName = $request->attributes->get('_route');
code:ph
public function get(string $key, mixed $default = null): mixed
{
return \array_key_exists($key, $this->parameters) ? $this->parameters$key : $default; }
composer show symfony/framework-bundle --tree
framework-bundle の下に symfony/http-foundation がぶら下がっている
---
Docker
ファイル権限の設定または修正
-----
事例
懸念点・注意が必要な点
メモリ消費量:
ob_start() を使用すると、レガシーコードが出力する全データ(HTMLや大きなJSON)を一度メモリに蓄積します。出力サイズが非常に大きい場合、PHPの memory_limit に達する可能性があります。
Symfonyの「HttpFoundation」コンポーネントの基本原則
Symfonyは、PHPのグローバル変数($_GET, $_POST)やグローバル関数(header(), echo)を直接扱うのではなく、すべてを Request/Responseオブジェクト に抽象化して扱うことを基本としています。
妥当性の根拠: Symfony公式のThe HttpFoundation Componentの解説では、「PHPの header() 関数や echo を直接使うことは、テストを困難にし、レスポンスの制御を失わせるため、必ず Response オブジェクトを返すべきである」とされています。
レガシーコードを実行した後に Response オブジェクトを生成して返すのは、この原則に則った正しい「カプセル化(現代化)」です。
StreamedResponseと通常のResponseの使い分け
今回、StreamedResponse から Response に変更された判断は、APIの信頼性向上において正解です。
StreamedResponse: 出力を少しずつ流し込む(ストリーム)ためのものです。PHPの性質上、一度でも出力を開始(echo や require 内の出力)してしまうと、後から HTTPステータスコードを変更することはできません(「headers already sent」エラーの原因になります)。
Response (バッファリング方式): ob_start() を使うことで、レガシーコードの処理が「正常に終わったか」「403を出したか」を最後まで見届けた上で、最後に Symfony が正しいヘッダーを送信できます。APIサーバとして「エラーなのに200を返す」という矛盾を防ぐには、この方式しかありません。
ヘッダーを明示的に指定し直す必要性
PHPの header() 関数でセットされたヘッダーは、Symfonyの Response オブジェクトとは完全に別管理されています。
妥当性の根拠: Symfonyの Response::send() メソッドは、自身が持つヘッダーリストを送信します。レガシーコード内で header('Content-Type: application/json') と書いても、Symfonyの Response オブジェクトがそれを認識していなければ、Symfonyはデフォルトの text/html を上書きで送ってしまう可能性があります。
そのため、headers_list() でレガシーが吐き出したヘッダーを吸い上げ、Response オブジェクトにマージする作業は、APIの挙動を保証するために不可欠です。
ob_start
この関数は出力のバッファリングをオンにします。 出力のバッファリングを有効にすると、 (ヘッダ以外の) スクリプトからの出力は実際には行われず、 代わりに内部バッファに保存されます。
(ヘッダ以外の)
ob_get_clean
chdir
1. HTTPステータスコードを「後出し」できる唯一の方法
PHPの仕組み上、一度でも出力を開始してしまうと、その瞬間にHTTPステータスコードは 200 OK(デフォルト)で送信が確定してしまいます。
ob_start() を使うことで、レガシーコード内の echo や print が即座に送信されるのを防ぎ、「すべて実行し終わった後で、最終的なステータスコードを確認してからレスポンスを開始する」 という制御が可能になります。
2. 「事故の防止」としての役割
レガシーコードでは、意図しない場所で空白やPHPの通知(Notice/Warning)が出力されてしまうことがありますが、これらは「意図しない出力」としてHTTPヘッダーを勝手に送信させてしまう原因になります。
ob_start() で包んでおけば、そうした「不純物」も $content に取り込まれるため、Symfonyがヘッダーを制御する権利を最後まで奪われずに済みます。
3. バッファ管理の正確性
ob_start() から ob_get_clean() までの流れは、PHPのバッファリング処理として最も標準的かつ安全な構成です。
ob_start(): バッファを積み上げる。
require: レガシーを動かし、バッファに溜める。
ob_get_clean(): バッファの中身を取り出しつつ、バッファ自体を閉じる。
この一連の流れにより、メモリリークやバッファの入れ子(ネスト)の崩れを防ぐことができます。
--------
The default value for this option is an empty array, meaning that the application can respond to any given host.
How to Configure Symfony to Work behind a Load Balancer or a Reverse Proxy
Kernel