ふーん...Laravelですかぁ?いいえ、Hyperfです!
はじめに
「Laravelの豊富な機能でサクサク開発したいけど、Symfonyの柔軟性も捨てがたい…それに、どうせならSlimみたいな軽さもほしい!」
そんなわがままなあなたに朗報です。
Hyperf
このフレームワーク、なんとLaravel、Symfony、Slimのいいとこどり(多分)しちゃってます。
しかも、PSR準拠でSwooleによる非同期処理まで搭載。
つまり、全部入り最強PHPフレームワーク、それがHyperfなんです!(異論は認める)
ちなみに、Hyperfって名前がまた厨二心をくすぐるんですよね。
Hyperって単語、なんかカッコよくないですか?(男子なら分かってくれると信じてる!)
というわけで、今回はHyperfがどんなフレームワークなのか、実際に触りながら解説していきたいと思います。
Hyperfとは?
Hyperfは、爆速かつ超柔軟なPHP CLIフレームワークです。
- 超高速: Swoole/SwowコルーチンでPHP-FPMより圧倒的なパフォーマンス
- 高柔軟性: 依存性注入でコンポーネントを自由自在に組み合わせ可能
- 多機能: マイクロサービス対応、豊富なライブラリで開発効率UP
- 実戦的: 大規模サービスで長年稼働、安定性も抜群
ザックリ言うと 「速くて、柔軟で、色々できて、安心して使える」 そんなPHPフレームワークなんです!
詳しくは公式サイトへGo!
さて問題です
以下のコードはどのフレームワークで書かれているでしょうか?
<?php
// 省略...
return new class extends Migration
{
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('email')->unique();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('users');
}
};
<?php
namespace App\Model;
// 省略...
class User extends Model
{
protected ?string $table = 'users';
protected array $fillable = [
'email',
'name',
];
protected array $casts = [
'id' => 'integer',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
}
<?php
namespace App\Controller;
// 省略...
class UserController {
public function create(CreateUserRequest $request): User
{
$user = new User();
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->save();
return $user;
}
}
正解は?
正解は… Hyperf です!
「え、これLaravelでしょ?」って思いました?(私も最初そう思いました)
だって、Laravelにそっくりですやん?
つまり、Laravel経験者ならHyperf余裕で使いこなせるってこと!
しかも、Swooleによる非同期処理で爆速!
Laravelの快適さとSwooleの速さ、両方手に入れた欲張りセット、それがHyperfなんです!
APIを作ってみる
それでは、実際にHyperfでAPIを作ってみましょう!
ちなみに、今回作ったサンプルは以下のリポジトリにあります。
1.プロジェクト作成
まずは、composer
でプロジェクトを作成します。
Hyperfでは、スケルトンを使ってプロジェクトを作成することができます。
composer create-project hyperf/hyperf-skeleton
create-project
を実行すると、質問形式でプロジェクトの初期設定を進められます。
Hyperfでは、RedisやElasticsearchなど、色々な拡張機能を選べますが、今回はデータベースを使うくらいで十分なので、サクッと設定します。
What time zone do you want to setup ?
[n] Default time zone for php.ini
Make your selection or type a time zone name, like Asia/Shanghai (n):
Asia/Tokyo
Do you want to use Database (MySQL Client) ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (yes): y
- Adding package hyperf/database (~3.1.0)
- Adding package hyperf/db-connection (~3.1.0)
Do you want to use Redis Client ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (yes): n
Which RPC protocol do you want to use ?
[1] JSON RPC with Service Governance
[2] JSON RPC
[3] gRPC
[n] None of the above
Make your selection or type a composer package name and version (n): n
Which config center do you want to use ?
[1] Apollo
[2] Aliyun ACM
[3] ETCD
[4] Nacos
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/constants component ?
[y] yes
[n] None of the above
Make your selection (n): y
- Adding package hyperf/constants (~3.1.0)
- Copying app/Constants/ErrorCode.php
- Copying app/Exception/BusinessException.php
Do you want to use hyperf/async-queue component ? (A simple redis queue component)
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/amqp component ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/model-cache component ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/elasticsearch component ?
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
Do you want to use hyperf/tracer component ? (An open tracing protocol component, adapte with Zipkin etc.)
[y] yes
[n] None of the above
Make your selection or type a composer package name and version (n): n
必要コンポーネントを追加
今回の開発では、Hot Reload的な機能やバリデーションを使うため、以下のコンポーネントを追加します。
composer require hyperf/watcher --dev
composer require hyperf/validation
2.docker-compose.yml
プロジェクトを作成すると、ルート直下にdocker-compose.yml
が生成されますので、そこにMySQLの設定を追加します。
services:
hyperf-skeleton:
container_name: hyperf-skeleton
image: hyperf-skeleton
build:
context: .
volumes:
- ./:/opt/www
ports:
- 9501:9501
environment:
- APP_ENV=dev
- SCAN_CACHEABLE=false
# 今回、開発用に``hyperf/watcher``というHot Reload的な機能を使うため、entrypointを追加
entrypoint: ['php', '/opt/www/bin/hyperf.php', 'server:watch']
# MySQLのコンテナが起動してからアプリケーションを起動するようにdepends_onを追加
depends_on:
- mysql
networks:
- hyperf-network
# MySQLの追加
mysql:
image: 'mysql/mysql-server:8.0'
ports:
- '3306:3306'
environment:
# Hyperfの.envファイルに記載したDB情報を環境変数に設定
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
MYSQL_ROOT_HOST: '%'
MYSQL_DATABASE: '${DB_DATABASE}'
MYSQL_USER: '${DB_USERNAME}'
MYSQL_PASSWORD: '${DB_PASSWORD}'
MYSQL_ALLOW_EMPTY_PASSWORD: 1
TZ: 'Asia/Tokyo'
volumes:
- './mysql:/var/lib/mysql'
healthcheck:
test: ['CMD', 'mysqladmin', 'ping', '-p${DB_PASSWORD}']
retries: 3
timeout: 5s
networks:
- hyperf-network
# 一応、ネットワークを追加
networks:
hyperf-network:
driver: bridge
docker-compose.yml
にMySQLの設定を追加できたら、次はdocker compose
でコンテナを起動します。
docker compose up -d hyperf-skeleton
コンテナの起動が完了したら、http://localhost:9501
にアクセスして、jsonが返ってくればOKです!
3.マイグレーション
さて、開発の準備が整ったので、次はデータベースです。
とりあえず、ユーザーテーブルのマイグレーションを作成します。
docker compose exec hyperf-skeleton php bin/hyperf.php gen:migration create_users_table
migrations
ディレクトリに2025_02_15_084751_create_users_table.php
が生成されるので、以下のように編集します。
マイグレーションファイルを作成したらmigrate
コマンドでテーブルを作成します。
docker compose exec hyperf-skeleton php bin/hyperf.php migrate
これで、データベースにusers
テーブルが作成されましたが、name
カラムがないので、追加のマイグレーションファイルを作成します。
docker compose exec hyperf-skeleton php bin/hyperf.php gen:migration add_name_to_users_table
migrations
ディレクトリに2025_02_15_110519_add_name_to_users_table.php
が生成されるので、以下のように編集します。
マイグレーションファイルを作成したらmigrate
コマンドでテーブルを更新します。
docker compose exec hyperf-skeleton php bin/hyperf.php migrate
これで、データベースにname
カラムが追加されました!
...もうお気づきですよね?
ここまでの流れ...Laravelとほぼ同じ!(というかほぼLaravel)
Laravelが得意な人は、Hyperfもすぐに使いこなせると思います!
4.モデル(ORM)
テーブルが作成できたら、次はモデルを作成します。
docker compose exec hyperf-skeleton php bin/hyperf.php gen:model users
app/Model/User.php
が生成されますので、$fillable
を以下のように編集します。
Hyperfのモデルは、LaravelのEloquent
モデルとほぼ同じなので、Laravelのモデルを使ったことがある人は、同じモデルの定義やメソッドで使えると思います。
5.コントローラ&ルーティング
プロセス常駐の確認
通常、PHPはリクエストごとに新しいプロセスを起動し、処理が完了するとプロセスを終了します。しかし、HyperfはSwooleを使って非同期処理を行うため、プロセスを常駐させることができます。
それを、コントローラで確認するために、すでに作成されているIndexController.php
を以下のように編集します。
ルーティングはconfig/routes.php
に設定済みです。
http://localhost:9501/
にアクセスすると、$count
が増加していくことが確認できましたか?
通常のPHPだと、static
変数はリクエストごとに初期化されるので、/
にアクセスするたびに$count
が初期化されるはずですが、Hyperfではプロセスが常駐しているため、$count
が初期化されずに増加していくことが確認できます。
つまり、Swooleなどのプロセスが常駐する場合は、メモリリークに気をつけないと大変なことに...!
Userコントローラ
次は、ユーザーのCRUD処理を行うコントローラを作成します。
今回は、Attributeを使ったルーティングも試します。
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Model\User;
use Hyperf\Collection\Collection;
use Hyperf\Database\Model\ModelNotFoundException;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\RequestMapping;
use Hyperf\HttpServer\Contract\RequestInterface;
// ControllerのAttributeを定義することで、直接コントローラにルーティングを定義できる
#[Controller]
class UserController
{
/**
* ユーザー一覧取得
*
* RequestMappingのAttributeを使って、HTTPリクエストを処理するメソッドを定義する
*
* @return Collection<integer,User>
*/
#[RequestMapping(path: "/user", methods: ["get"])]
public function index(): Collection
{
$users = User::get();
return $users;
}
/**
* ユーザー作成
*
* @param RequestInterface $request
* @return User
*/
#[RequestMapping(path: "/user", methods: ["post"])]
public function create(RequestInterface $request): User
{
// この辺のRequestとモデルの扱い方もLaravelとほぼ一緒ですね
$user = new User();
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->save();
return $user;
}
/**
* ユーザー更新
*
* @param RequestInterface $request
* @param string $id
* @return User
* @throws ModelNotFoundException
*/
#[RequestMapping(path: "/user/{id}", methods: ["put"])]
public function update(
RequestInterface $request,
string $id,
): User {
$user = User::findOrFail($id);
$user->name = $request->input('name');
$user->save();
return $user;
}
// 省略...
}
http://localhost:9501/user
にアクセスすると、ユーザー一覧が返ってくることが確認できます(事前にユーザーレコードを作成してから確認してね)
RequestMapping
のAttributeを使うことで、コントローラにルーティングを直接定義することができますが、config/routes.php
にルーティングを定義することもできるので、好みに合わせて使い分けるといいでしょう。
6.バリデーション
基本的なCRUD処理ができるようになったので、次はバリデーションを追加します。
LaravelのバリデーションといえばFormRequest
ですが、Hyperfでも同様の機能を使うことができます。
ミドルウェアとExceptionHandlerの設定
とりあえず、hyperf/validation
を使ってバリデーションをするので、必要なミドルウェアとExceptionHandlerを追加します。
今回jsonでエラーレスポンスを返すため、ドキュメントの設定と違い自前実装したAppValidationExceptionHandlerを追加しています。
FormRequestの作成
ユーザー作成&更新のCreateUserRequest
とUpdateUserRequest
を作成してみましょう。
docker compose exec hyperf-skeleton php bin/hyperf.php gen:request CreateUserRequest
docker compose exec hyperf-skeleton php bin/hyperf.php gen:request UpdateUserRequest
app/Request/CreateUserRequest.php
とapp/Request/UpdateUserRequest.php
が生成されるので、以下のように編集します。
主にemail
とname
のバリデーションを追加しています。
UserController
では、以下のようにメソッドインジェクションを使ってバリデーションを行うように修正します。
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Request\CreateUserRequest;
use App\Request\UpdateUserRequest;
// 省略...
#[Controller]
class UserController
{
// 省略...
/**
* @param CreateUserRequest $request
* @return User
*/
#[RequestMapping(path: "/user", methods: ["post"])]
public function create(CreateUserRequest $request): User
{
$user = new User();
$user->name = $request->input('name');
$user->email = $request->input('email');
$user->save();
return $user;
}
/**
* @param UpdateUserRequest $request
* @param string $id
* @return User
* @throws ModelNotFoundException
*/
#[RequestMapping(path: "/user/{id}", methods: ["put"])]
public function update(
UpdateUserRequest $request,
string $id,
): User {
$user = User::findOrFail($id);
$user->name = $request->input('name');
$user->save();
return $user;
}
// 省略...
}
Laravelと同じように、翻訳ファイルを使ってエラーメッセージをカスタマイズすることもできますが、今回は省略します。
7.DependencyInjection
Laravelといえば、強力なDIコンテナですが、Hyperfでも同様の機能を使うことができます。
例えば、以下のようなサービスクラスを作成してUserController
に注入することができます。
UserController
では、FormRequest
と同じようにメソッドインジェクションでサービスクラスを注入することができます。
もちろんですが、コンストラクタインジェクションでも注入可能です。
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Request\CreateUserRequest;
use App\Service\AlreadyUserExistsException;
use App\Service\CreateUserInput;
use App\Service\CreateUserService;
// 省略...
#[Controller]
class UserController
{
// 省略...
/**
* @param CreateUserService $service
* @param CreateUserRequest $request
* @return User
*/
#[RequestMapping(path: "/user", methods: ["post"])]
public function create(CreateUserService $service, CreateUserRequest $request): User
{
try {
$user = $service->execute(
new CreateUserInput(
name: $request->input('name'),
email: $request->input('email'),
),
);
} catch (AlreadyUserExistsException $e) {
throw new BadRequestHttpException($e->getMessage());
}
return $user;
}
}
config/autoload/dependencies.php
にインタフェースと実装クラスのバインドを追加することで、DIコンテナにサービスクラスを登録することもできます。
8.テスト
最後に、テストを書いてみましょう。
まとめ
Hyperfは、Laravelのような開発効率、Symfonyのような柔軟性、Slimのような軽量さ、そしてSwooleによる爆速処理を兼ね備えた、欲張りなフレームワークです。
さらに、PSRに準拠することで、高い互換性を実現し、コンポーネントの再利用性を高めています。
Laravel経験者ならすぐに使いこなせるはずですし、Laravelを知らない人でも、Hyperfを使えば最速でWebアプリ開発を体験できるでしょう。
ぜひ一度、Hyperfを試してみてください!
Discussion