TempestはPHP界に「嵐」を巻き起こすか? 新星フレームワークで遊んでみた!
はじめに
PHPフレームワーク戦国時代に、また一つ新たな風が吹きました。その名もTempest(嵐)。名前からして何やらすごそうですが、果たしてその実力は?
この記事では、この新進気鋭のPHPフレームワークTempestを使い、簡単なアプリ作成からデプロイまで、その魅力と実力を探っていきます!
Tempestって何者?
公式ドキュメントから抜粋
Tempestは、PHP開発のためのフレームワークで、開発者の邪魔にならないように設計されています。その核となる理念は、フレームワークに煩わされることなく、アプリケーションコードに集中できるようにすることです。
なるほど...?つまり、LaravelやSymfonyのような「至れり尽くせり」なフルスタックフレームワークが時に課してくる"お作法"や"独自ルール"から解放され、もっと自由に、もっとピュアなPHPでアプリが作れる...ということでしょうか?
もし本当にそうなら、試さない手はありません。
技術スタック
- フレームワーク: Tempest 1.6
- PHPバージョン: 8.4
- データベース: SQLite
- コンテナイメージ: FrankenPHP (Classicモード)
- IaC: Pulumi
準備
Tempestは最新のPHPの機能をふんだんに使っているため、PHP 8.4以上が必須です。
devboxでの環境構築
私は普段macOSで開発しているのですが、PHPのバージョン管理が面倒なので、devboxを利用して環境構築しました。
1. devboxのインストール
devboxをインストールしてない場合は、以下のコマンドでインストールします。
curl -fsSL https://get.jetify.com/devbox | bash
2. devboxの初期化
プロジェクト用のディレクトリを作り、devboxを初期化します
devbox init
3. PHPの追加
PHP 8.4とcomposerなどの環境を追加します。
devbox add php@8.4.11 php84Packages.composer@latest php84Packages.phpstan@latest
4. devboxシェルの起動
devboxのシェルを起動することで、シェルの中でPHP 8.4が利用可能になります。
devbox shell
Tempestのプロジェクト作成
準備は整いました。いよいよTempestプロジェクトを作成します。
私はインフラ管理にPulumiを使う予定なので、src
ディレクトリの中にプロジェクトを作ることにします。
composer create-project tempest/app src
作成に成功したら、src
ディレクトリに移動して、以下コマンドでビルトインサーバーを起動します。
cd src
php tempest serve
ブラウザでhttp://localhost:8000
にアクセスして、Tempestのウェルカムページが表示されれば成功です!
色々試してみる
0. ディレクトリ構成変更
デフォルトのディレクトリ構成はこんな感じ。
.
├── .tempest/
├── app/
│ ├── HelloCommand.php
│ ├── home.view.php
│ ├── HomeController.php
│ └── x-base.view.php
├── public/
├── tests/
├── vendor/
├── .env
├── composer.json
├── composer.lock
└── tempest
app
ディレクトリにControllerもViewもごちゃ混ぜなのが少し気になります。
そこで、私好みに整理整頓してみました。
.
├── .tempest/
├── app/
│ ├── Command/
│ └── Controller/
├── config/
├── public/
├── tests/
├── vendor/
├── view/
├── .env
├── composer.json
├── composer.lock
└── tempest
view
やconfig
を外に出し、app
には純粋なアプリケーションロジックだけを置くスタイルです。
「え、そんな勝手なことして大丈夫?」と思いますよね。大丈夫なんです(たぶん)この変更は公式ドキュメントには載っていないので自己責任ですが、composer.json
を少し書き換えるだけで対応できます。
"autoload": {
"psr-4": {
"App\\": "app/",
"View\\": "view/",
"Config\\": "config/"
}
},
後に説明するのですが、この設定を行うことで、TempestのDiscovery機能により自動的にview
やconfig
ディレクトリを認識してくれます。
1. コントローラー作成&ルーティング設定
まずは、tempest
コマンドでapp/Controller
以下にコントローラーを作成します。
php tempest make:controller Controller/JsonController
生成されたapp/Controller/JsonController.php
がこちら。
<?php
namespace App\Controller;
use Tempest\Router\Get;
use Tempest\View\View;
use function Tempest\view;
final class JsonController
{
#[Get(uri: '/dummy-path')]
public function __invoke(): View
{
return view('dummy-view');
}
}
このコントローラーは、/dummy-path
にGETリクエストが来たときに、dummy-view
テンプレートをレンダリングして返すだけのシンプルなものです。
Tempestでは、メソッドに対してGet
, Post
, Put
, Delete
アトリビュートを設定することでルーティングすることができます(今風ですね!)
これをJsonを返すように書き換えてみましょう。
<?php
declare(strict_types=1);
namespace App\Controller;
use Tempest\Http\ContentType;
use Tempest\Http\Response;
use Tempest\Http\Responses\Ok;
use Tempest\Router\Get;
final class JsonController
{
#[Get(uri: '/json')]
public function __invoke(): Response
{
return new Ok([
'message' => 'Hello, World!',
'status' => 'success',
'data' => [
'example' => 'This is a JSON response from the JsonController.',
],
])->setContentType(ContentType::JSON);
}
}
試しにcurl
でアクセスしてみると、以下のようにJsonが返ってきます。
curl http://localhost:8000/json | jq
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 116 0 116 0 0 1336 0 --:--:-- --:--:-- --:--:-- 1348
{
"message": "Hello, World!",
"status": "success",
"data": {
"example": "This is a JSON response from the JsonController."
}
}
ちなみに、追加したルートの一覧を確認するには、以下のコマンドを実行します。
php tempest routes
// REGISTERED ROUTES
These routes are registered in your application.
GET / ............................................................ App\Controller\HomeController::__invoke()
GET /json ........................................................ App\Controller\JsonController::__invoke()
2. コマンド作成
次に、tempest
コマンドでapp/Command
以下にコマンドを作成します。
php tempest make:command Command/GreetCommand
コマンドを実行することで、app/Command/GreetCommand.php
が作成されます。
<?php
namespace App\Command;
use Tempest\Console\Console;
use Tempest\Console\ConsoleCommand;
final class GreetCommand
{
public function __construct(
private Console $console,
) {
}
#[ConsoleCommand(name: 'command-greet-command')]
public function __invoke(): void
{
$this->console->success('Done!');
}
}
このコマンドは、command-greet-command
という名前で実行でき、実行するとDone!
と表示されるだけのシンプルなものです。
生成されたファイルに#[ConsoleCommand]
アトリビュートを使ってコマンドを定義します。これも直感的でいいですね。
とりあえず、挨拶を返すように修正してみます。
<?php
namespace App\Command;
use Tempest\Console\Console;
use Tempest\Console\ConsoleCommand;
final class GreetCommand
{
public function __construct(
private Console $console,
) {}
#[ConsoleCommand(
name: 'greet',
description: 'Outputs a greeting message.',
)]
public function greet(string $name = 'stranger'): void
{
$this->console->success("Hello, {$name}!");
}
}
実行してみましょう。
php tempest greet --name=Tempest
✓ // Hello, Tempest!
ちなみに、追加しコマンドの詳細を確認するには、以下のコマンドを実行します。
php tempest greet --help
// GREET
Outputs a greeting message.
// USAGE
greet [name=stranger]
お気付きでしょうか?
ここで、勘の良い方は気付いたかもしれません。
私は独断でディレクトリ構成を変更しました。app
ディレクトリにあったControllerをapp/Controller
に移動し、view
やconfig
も完全に別の場所に配置しました。
なのに、なぜフレームワークはそれらを正しく認識しているのでしょうか?
これがTempestのDiscovery(検出)機能です!
Tempestは、composer.json
のautoload
設定などをヒントに、プロジェクト内のController、Command、設定ファイルなどを自動的に探し出して登録してくれるのです。
これにより、私たちは面倒な設定ファイルから解放され、より自由に、直感的に開発を進めることができます。
この開発体験は、まさに「嵐」級のインパクトです!
(本番環境ではパフォーマンスのためにキャッシュが推奨されています)
アプリ作成
Tempestのすごさがわかってきたところで、簡単なユーザー管理アプリをサクッと作ってみます。
1. データベース接続設定とマイグレーション
今回はお手軽なSQLiteを使います。まず、config/database.config.php
に設定を記述。
次に、マイグレーションファイルを作成します。
php tempest make:migration Migration/CreateUserTable
生成されたMigration/CreateUserTable.php
のテーブル定義を修正...
マイグレーション実行!
php tempest migrate:up
マイグレーションが成功すると、database.sqlite
ファイルが作成され、users
テーブルが作成されます。
2. ユーザーモデルとリポジトリ作成
次に、ユーザーモデルとリポジトリを作成します。
php tempest make:model Repository/User
生成されたapp/Repository/User.php
を修正...
リポジトリ生成のコマンドはないので、ドキュメントを参考に手動で作成します。
3. コントローラーとリクエスト作成
次に、ユーザー管理用のコントローラーとリクエストを作成します。
php tempest make:controller Controller/UserController
生成されたapp/Controller/UserController.php
を修正...
メソッドの引数にUserRepository
と書くだけで、自動的にインスタンスが注入(DI)されます。
バリデーション付きのリクエストも作ります。
php tempest make:request Request/CreateUserRequest
必要十分なバリデーションルールが揃っています。
4. ビュー作成
ユーザー一覧画面のビューファイルview/users.view.php
を作成します。
以下のような画面が表示されます。
5. ミドルウェア作成
Laravelなどのフルスタックフレームワークを利用していると、WebフォームでPUTやDELETEメソッドを使いたい時、<input type="hidden" name="_method" value="PUT">
というお作法、ありますよね?Tempestにはデフォルトでこの機能がないので、ミドルウェアで自作してみましょう。
php tempest make:middleware Middleware/SetFormMethod
生成されたapp/Middleware/SetFormMethod.php
を修正...
ミドルウェアには優先度を設定できるのですが、ルーティングのマッチングの前に実行されるように、ミドルウェアの実行優先度設定であるPriority
アトリビュートを調整します。
6. テスト作成
最後に、ユーザー管理用のテストを作成します。
テスト生成のコマンドはないので、キュメントを参考に手動で作成します。
テストは、以下のコマンドでPHPUnitを利用して実行します。
composer phpunit
テストは、少し書きにくい感じがしましたが、基本的な部分は抑えられているので、必要十分だと思います。
デプロイ
今回は、AWS Fargateにデプロイしてみます。
今回インフラ部分の詳細は割愛しますが、詳細は今回作成したGitHubリポジトリを参照してください。
1. Dockerfile作成
今回は、高速で有名なfrankenphp
の公式イメージをベースに、Classicモードで動かします。
Dockerfile
はこんな感じ。
2. Pulumiプロジェクト作成
IaCツールPulumiを使って、Fargateの環境をコードで構築します。詳細は割愛しますが、pulumi up
コマンド一発でデプロイ完了!
無事、Fargate上でTempestアプリが元気に動いていることを確認できました
まとめ
Tempestを触ってみて感じたのは、「ちょうどいい」 という心地よさでした。
LaravelやSymfonyのようなフルスタックフレームワークが持つ圧倒的な機能群はありません。しかし、その代わりに、開発者を縛らない自由さと、驚くほど直感的な開発体験があります。
特に、あのDiscovery機能は革命的です。ディレクトリ構造を自由にいじれる開放感は、一度味わうと病みつきになるかもしれません。
DIコンテナ、ルーティング、ミドルウェア、バリデーション、テスト...アプリ開発に必要な最小限の武器はすべて揃っています。公式ドキュメントも分かりやすく、学習コストも低い。
Tempestはまだ若いフレームワークですが、PHP開発の「当たり前」を変えるポテンシャルを秘めています。
今後、Tempestがどのように発展していくのか楽しみです。
Discussion