😇

ふーん...Laravelですかぁ?いいえ、Hyperfです!

2025/02/15に公開

はじめに

「Laravelの豊富な機能でサクサク開発したいけど、Symfonyの柔軟性も捨てがたい…それに、どうせならSlimみたいな軽さもほしい!」

そんなわがままなあなたに朗報です。

Hyperf

このフレームワーク、なんとLaravelSymfonySlimのいいとこどり(多分)しちゃってます。

しかも、PSR準拠Swooleによる非同期処理まで搭載。

つまり、全部入り最強PHPフレームワーク、それがHyperfなんです!(異論は認める)

ちなみに、Hyperfって名前がまた厨二心をくすぐるんですよね。

Hyperって単語、なんかカッコよくないですか?(男子なら分かってくれると信じてる!)

というわけで、今回はHyperfがどんなフレームワークなのか、実際に触りながら解説していきたいと思います。

Hyperfとは?

Hyperfは、爆速かつ超柔軟なPHP CLIフレームワークです。

  • 超高速: Swoole/SwowコルーチンでPHP-FPMより圧倒的なパフォーマンス
  • 高柔軟性: 依存性注入でコンポーネントを自由自在に組み合わせ可能
  • 多機能: マイクロサービス対応、豊富なライブラリで開発効率UP
  • 実戦的: 大規模サービスで長年稼働、安定性も抜群

ザックリ言うと 「速くて、柔軟で、色々できて、安心して使える」 そんなPHPフレームワークなんです!

詳しくは公式サイトへGo!
https://hyperf.io/

さて問題です

以下のコードはどのフレームワークで書かれているでしょうか?

<?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を作ってみましょう!

ちなみに、今回作ったサンプルは以下のリポジトリにあります。
https://github.com/takemo101/hyperf-example

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が生成されるので、以下のように編集します。

https://github.com/takemo101/hyperf-example/blob/f54b09a7d814b4f1911b494852e3c6c3af42b65c/migrations/2025_02_15_084751_create_users_table.php#L1-L28

マイグレーションファイルを作成したら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が生成されるので、以下のように編集します。

https://github.com/takemo101/hyperf-example/blob/f54b09a7d814b4f1911b494852e3c6c3af42b65c/migrations/2025_02_15_110519_add_name_to_users_table.php#L1-L28

マイグレーションファイルを作成したら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を以下のように編集します。

https://github.com/takemo101/hyperf-example/blob/f54b09a7d814b4f1911b494852e3c6c3af42b65c/app/Model/User.php#L1-L39

Hyperfのモデルは、LaravelのEloquentモデルとほぼ同じなので、Laravelのモデルを使ったことがある人は、同じモデルの定義やメソッドで使えると思います。

5.コントローラ&ルーティング

プロセス常駐の確認

通常、PHPはリクエストごとに新しいプロセスを起動し、処理が完了するとプロセスを終了します。しかし、HyperfはSwooleを使って非同期処理を行うため、プロセスを常駐させることができます。
それを、コントローラで確認するために、すでに作成されているIndexController.phpを以下のように編集します。

https://github.com/takemo101/hyperf-example/blob/f54b09a7d814b4f1911b494852e3c6c3af42b65c/app/Controller/IndexController.php#L1-L28

ルーティングはconfig/routes.phpに設定済みです。

https://github.com/takemo101/hyperf-example/blob/f54b09a7d814b4f1911b494852e3c6c3af42b65c/config/routes.php#L15

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を追加します。
https://github.com/takemo101/hyperf-example/blob/9e0490eeb3b4a002636e6b3fc20e3d55ce9f8263/config/autoload/middlewares.php#L1-L16
https://github.com/takemo101/hyperf-example/blob/6b08198be63fdd5eea736d9899a334c142494cdd/config/autoload/exceptions.php#L1-L21

今回jsonでエラーレスポンスを返すため、ドキュメントの設定と違い自前実装したAppValidationExceptionHandlerを追加しています。

FormRequestの作成

ユーザー作成&更新のCreateUserRequestUpdateUserRequestを作成してみましょう。

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.phpapp/Request/UpdateUserRequest.phpが生成されるので、以下のように編集します。
主にemailnameのバリデーションを追加しています。
https://github.com/takemo101/hyperf-example/blob/6b08198be63fdd5eea736d9899a334c142494cdd/app/Request/CreateUserRequest.php#L1-L33
https://github.com/takemo101/hyperf-example/blob/6b08198be63fdd5eea736d9899a334c142494cdd/app/Request/UpdateUserRequest.php#L1-L28
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に注入することができます。

https://github.com/takemo101/hyperf-example/blob/fd85faa4356bb8052026a018770b7a81fa2252bf/app/Service/CreateUserService.php#L1-L41

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.テスト

最後に、テストを書いてみましょう。
https://github.com/takemo101/hyperf-example/blob/16470e526afbbaade44a84adcc9c8fcba668c638/test/Cases/UserTest.php#L1-L121

まとめ

Hyperfは、Laravelのような開発効率、Symfonyのような柔軟性、Slimのような軽量さ、そしてSwooleによる爆速処理を兼ね備えた、欲張りなフレームワークです。

さらに、PSRに準拠することで、高い互換性を実現し、コンポーネントの再利用性を高めています。

Laravel経験者ならすぐに使いこなせるはずですし、Laravelを知らない人でも、Hyperfを使えば最速でWebアプリ開発を体験できるでしょう。

ぜひ一度、Hyperfを試してみてください!

株式会社ソニックムーブ

Discussion