ixbox / phambda
Installs: 89
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 1
Forks: 0
Open Issues: 0
pkg:composer/ixbox/phambda
Requires
- php: ^8.1
- php-http/discovery: ^1.17
- psr/container: ^2.0
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-server-handler: ^1.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.71
- mockery/mockery: ^1.6
- nyholm/psr7: ^1.8
- pestphp/pest: ^2.36
- php-http/curl-client: ^2.3
- phpstan/phpstan: ^2.1
- rector/rector: ^2.0
README
概要
Phambda は、PHP で AWS Lambda を簡単に利用するためのランタイムを提供します。このパッケージを使用することで、AWS Lambda 上で PHP コードを実行するための設定や実装が簡素化されます。
主な特徴
- PSR-7 および PSR-15 に準拠したリクエスト/レスポンス処理
- 標準的な PHP インターフェースを使用することで、既存の PHP アプリケーションとの互換性を確保
- ミドルウェアパターンによる柔軟な処理の追加が可能
- AWS Lambda 環境に最適化されたランタイム
- コールドスタートの最小化
- メモリ使用の効率化
- エラーハンドリングの強化
- static-php-cli 対応
- コンテナ化不要: 静的ビルドされた PHP バイナリを使用して直接デプロイ
- 軽量なデプロイパッケージで高速なコールドスタートを実現
- Docker 環境が不要なため、CI/CD パイプラインが簡素化
- シンプルなインターフェースでの実装
- 最小限のコードで Lambda 関数を作成可能
- 豊富なログ機能により、デバッグとモニタリングが容易
インストール方法
以下のコマンドを実行してインストールします:
composer require ixbox/phambda
必要な環境
- PHP 8.1 以上
- Composer
- AWS Lambda の実行権限を持つ IAM ロール
- 基本的な Lambda 実行権限
- CloudWatch Logs へのアクセス権限
AWS Lambda 環境のセットアップ
Phambda は static-php-cli を活用することで、コンテナ化せずに AWS Lambda にデプロイできます。static-php-cli は PHP を静的にリンクしたシングルバイナリを提供しており、AWS Lambda のカスタムランタイム(provided.al2023)で直接実行可能です。
static-php-cli を使用したデプロイ(推奨)
この方法では Docker イメージのビルドが不要で、デプロイパッケージが軽量になり、コールドスタートが高速化されます。
Lambda Layer を活用した構成により、PHP バイナリ(約 30MB)をアプリケーションコードから分離し、zip の 50MB 制限内に収めやすくなります。
┌─────────────────────────────┐
│ Lambda 関数(< 50MB) │ ← アプリコード + vendor のみ
├─────────────────────────────┤
│ PHP Layer(~30MB) │ ← static-php-cli バイナリ
└─────────────────────────────┘
-
SAM テンプレートの設定
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Resources: # PHP Runtime Layer (static-php-cli) PhpLayer: Type: AWS::Serverless::LayerVersion Properties: LayerName: php-runtime Description: PHP 8.4 runtime built with static-php-cli ContentUri: . CompatibleRuntimes: - provided.al2023 CompatibleArchitectures: - arm64 # または x86_64 Metadata: BuildMethod: makefile BuildArchitecture: arm64 App: Type: AWS::Serverless::Function Properties: Runtime: provided.al2023 Handler: bootstrap Architectures: - arm64 Layers: - !Ref PhpLayer Events: HelloWorld: Type: HttpApi Properties: Path: /hello Method: get Metadata: BuildMethod: makefile
-
Makefile の作成
PHP_VERSION = 8.4.15 # Detect architecture from build container (template.yaml の Architectures に連動) ARCH = $(shell uname -m) PHP_ARCHIVE = php-$(PHP_VERSION)-cli-linux-$(ARCH).tar.gz PHP_URL = https://dl.static-php.dev/static-php-cli/common/$(PHP_ARCHIVE) # Download PHP binary matching the target architecture prepare: @echo "Detected architecture: $(ARCH)" [ -e $(PHP_ARCHIVE) ] || curl -fsSL -o $(PHP_ARCHIVE) $(PHP_URL) tar -xzf $(PHP_ARCHIVE) chmod +x php [ -e composer.phar ] || curl -fsSL -O https://getcomposer.org/download/latest-stable/composer.phar # Build PHP Layer (Layer is mounted at /opt, so php will be at /opt/bin/php) build-PhpLayer: prepare mkdir -p $(ARTIFACTS_DIR)/bin cp php $(ARTIFACTS_DIR)/bin/php # Build Application Function (code + vendor only, no PHP binary) build-App: prepare cp -r src $(ARTIFACTS_DIR)/src cp composer.json composer.lock $(ARTIFACTS_DIR) cp bootstrap $(ARTIFACTS_DIR) cp main.php $(ARTIFACTS_DIR) ./php composer.phar install --no-interaction --no-progress --prefer-dist --optimize-autoloader -d $(ARTIFACTS_DIR) # Remove unnecessary files to reduce package size find $(ARTIFACTS_DIR)/vendor -type f -name "*.md" -delete find $(ARTIFACTS_DIR)/vendor -type d -name "tests" -exec rm -rf {} + 2>/dev/null || true
Note: 本番環境では
--no-devを使用し、必要な PSR 実装(HTTP クライアント、PSR-7 実装など)をcomposer.jsonのrequireセクションに追加してください。 -
bootstrap ファイルの作成
#!/bin/bash # PHP binary is provided by the Layer at /opt/bin/php /opt/bin/php /var/task/main.php
-
デプロイ
# --use-container でコンテナ内ビルド(Linux 環境でビルドするため必須) sam build --use-container sam deploy --guided
static-php-cli + Layer のメリット
- コンテナ不要: Docker イメージのビルド・プッシュが不要
- 50MB 制限に対応: PHP バイナリを Layer に分離し、関数 zip を軽量化
- デプロイ高速化: PHP Layer は初回のみ、以降はアプリコードだけ更新
- 高速なコールドスタート: コンテナ起動のオーバーヘッドがない
- シンプルな CI/CD: Docker 環境が不要なため、ビルドパイプラインが簡素化
- Layer 共有: 複数の Lambda 関数で同じ PHP Layer を再利用可能
コンテナを使用したデプロイ(代替方法)
Docker コンテナを使用したい場合は、以下の方法も利用可能です:
-
Lambda 関数の作成
- ランタイム: カスタムランタイム
- アーキテクチャ: x86_64 または arm64
- メモリ: 推奨 256MB 以上
- タイムアウト: アプリケーションの要件に応じて設定(デフォルト 29 秒)
-
デプロイメントパッケージの作成
# 本番環境用の依存関係のみをインストール composer install --no-dev --optimize-autoloader # デプロイメントパッケージの作成 zip -r function.zip . -x "*.git*" "tests/*" "*.dist" "*.md"
-
Lambda 関数の設定
- ハンドラー:
index.php(エントリーポイントとなる PHP ファイル) - 環境変数(必要に応じて設定):
AWS_LAMBDA_RUNTIME_API: Lambda ランタイム API のエンドポイント LOG_TIMEZONE: ログのタイムゾーン(デフォルト: UTC)
- ハンドラー:
使用方法
基本的な Hello World の例
以下は、AWS Lambda 上で動作する簡単な Hello World の例です:
#!/usr/bin/env php <?php require __DIR__ . '/vendor/autoload.php'; use Nyholm\Psr7\Response; use Phambda\Http\Runtime; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; $handler = new class implements RequestHandlerInterface { public function handle(ServerRequestInterface $request): ResponseInterface { return new Response(200, [], "Hello World!"); } }; $runtime = new Runtime($handler); $runtime->run();
ロギング機能を使用した例
以下は、ロギング機能を活用した実装例です:
#!/usr/bin/env php <?php require __DIR__ . '/vendor/autoload.php'; use Nyholm\Psr7\Response; use Phambda\Context; use Phambda\Http\Runtime; use Phambda\Logger\AbstractLogger; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\LoggerInterface; class MyHandler implements RequestHandlerInterface { public function __construct( private readonly LoggerInterface $logger ) {} public function handle(ServerRequestInterface $request): ResponseInterface { // リクエストIDの取得 $requestId = $request->getAttribute('awsRequestId'); /** @var \Phambda\Context $context */ $context = $request->getAttribute('lambda-context'); $this->logger->info('リクエスト処理開始', [ 'request_id' => $requestId, 'function' => [ 'name' => $context->functionName, 'version' => $context->functionVersion, 'memory' => $context->memoryLimitInMb ] ]); // 処理ロジック $data = [ 'message' => 'Hello from Phambda!', 'timestamp' => time(), 'request_id' => $requestId, ]; $this->logger->info('レスポンス送信', ['data' => $data]); return new Response( status: 200, headers: [ 'Content-Type' => 'application/json', 'X-Request-ID' => $requestId ], body: json_encode($data) ); } } // ロガーの作成 $logger = new AbstractLogger(); // ハンドラーの作成とランタイムの実行 $handler = new MyHandler($logger); $runtime = new Runtime($handler); $runtime->run();
セキュリティヘッダーを設定した例
以下は、セキュリティを考慮したヘッダー設定の例です:
public function handle(ServerRequestInterface $request): ResponseInterface { $requestId = $request->getAttribute('awsRequestId'); $context = $request->getAttribute('lambda-context'); $responseData = [ 'message' => 'Hello from Phambda!', 'timestamp' => time(), 'request_id' => $requestId, ]; // セキュリティとトレーサビリティを考慮したヘッダー return new Response( status: 200, headers: [ 'Content-Type' => 'application/json', 'X-Request-ID' => $requestId, 'X-Function-Name' => $context->functionName, 'X-Function-Version' => $context->functionVersion, 'X-Content-Type-Options' => 'nosniff', 'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains', 'Cache-Control' => 'no-store, max-age=0', ], body: json_encode($responseData) ); }
エラーハンドリングの例
public function handle(ServerRequestInterface $request): ResponseInterface { try { // 処理ロジック $result = $this->processRequest($request); return new Response( status: 200, headers: ['Content-Type' => 'application/json'], body: json_encode(['data' => $result]) ); } catch (\InvalidArgumentException $e) { $this->logger->warning('不正なリクエスト', [ 'error' => $e->getMessage(), 'request_id' => $request->getAttribute('awsRequestId') ]); return new Response( status: 400, headers: ['Content-Type' => 'application/json'], body: json_encode([ 'error' => 'Bad Request', 'message' => $e->getMessage() ]) ); } catch (\Exception $e) { $this->logger->error('内部エラー', [ 'error' => $e->getMessage(), 'trace' => $e->getTraceAsString(), 'request_id' => $request->getAttribute('awsRequestId') ]); return new Response( status: 500, headers: ['Content-Type' => 'application/json'], body: json_encode([ 'error' => 'Internal Server Error', 'request_id' => $request->getAttribute('awsRequestId') ]) ); } }
設定オプション
Phambda は以下の設定オプションをサポートしています:
環境変数
| 環境変数 | 説明 | デフォルト値 |
|---|---|---|
AWS_LAMBDA_RUNTIME_API |
Lambda ランタイム API のエンドポイント | 127.0.0.1:9001 |
AWS_LAMBDA_FUNCTION_NAME |
Lambda 関数名 | - |
AWS_LAMBDA_FUNCTION_VERSION |
Lambda 関数のバージョン | - |
AWS_LAMBDA_FUNCTION_MEMORY_SIZE |
Lambda 関数のメモリサイズ | - |
AWS_LAMBDA_LOG_GROUP_NAME |
CloudWatch ログのグループ名 | - |
AWS_LAMBDA_LOG_STREAM_NAME |
CloudWatch ログのストリーム名 | - |
LOG_TIMEZONE |
ログのタイムゾーン | UTC |
ランタイム設定
// ランタイム設定のカスタマイズ $config = [ 'logger' => $customLogger, 'errorHandler' => $customErrorHandler, 'timeout' => 15, // 秒単位 ]; $runtime = new Runtime($handler, $config); $runtime->run();
AWS Lambda 特有の考慮事項
コールドスタート対策
Lambda 関数は、一定期間使用されないとコンテナが破棄され、次回実行時に新しいコンテナが起動します(コールドスタート)。これにより初回実行時のレイテンシが増加します。
対策:
- メモリ割り当てを増やす(256MB 以上推奨)
- 初期化コードをハンドラー関数の外に配置する
- 定期的なウォームアップ呼び出しを設定する
- Provisioned Concurrency を使用する
// 初期化コードをハンドラー外に配置 $db = new Database(); // コールドスタート時のみ実行される $handler = new class($db) implements RequestHandlerInterface { private $db; public function __construct($db) { $this->db = $db; } public function handle(ServerRequestInterface $request): ResponseInterface { // ハンドラーロジック } };
メモリ設定の最適化
- 256MB: 基本的な API 処理、シンプルなウェブアプリケーション
- 512MB: 中規模のアプリケーション、軽量な画像処理
- 1024MB 以上: 大規模なデータ処理、複雑な計算、画像処理
タイムアウト設定
Lambda 関数のデフォルトタイムアウトは 29 秒ですが、Phambda はデフォルトで 28 秒に設定しています(安全マージン確保のため)。長時間実行が必要な処理は、複数の関数に分割するか、Step Functions などを使用して実装することを検討してください。
デバッグとトラブルシューティング
ログの確認方法
Lambda 関数のログは CloudWatch Logs に保存されます。以下の方法でログを確認できます:
- AWS マネジメントコンソールから CloudWatch Logs にアクセス
/aws/lambda/[関数名]のロググループを選択- 最新のログストリームを選択して詳細を確認
一般的なエラーと解決方法
| エラー | 考えられる原因 | 解決方法 |
|---|---|---|
Execution timed out |
関数の実行時間が長すぎる | タイムアウト設定の見直し、処理の最適化 |
Memory size exceeded |
メモリ使用量が割り当てを超えている | メモリ割り当ての増加、メモリリークの修正 |
Unable to import module 'index' |
エントリーポイントが見つからない | ファイル名とハンドラー設定の確認 |
Internal server error |
未処理の例外が発生 | エラーハンドリングの実装、ログの確認 |
パフォーマンスチューニング
-
依存関係の最適化
- 不要なパッケージを削除
- Composer のオートローダーを最適化 (
--optimize-autoloader)
-
メモリ使用量の監視
$this->logger->info('メモリ使用量', [ 'used' => memory_get_usage(true) / 1024 / 1024 . ' MB', 'peak' => memory_get_peak_usage(true) / 1024 / 1024 . ' MB', ]);
-
実行時間の計測
$startTime = microtime(true); // 処理 $executionTime = microtime(true) - $startTime; $this->logger->info('実行時間', ['time' => $executionTime . ' 秒']);
ライセンス
このプロジェクトは MIT ライセンスの下で提供されています。
コントリビューション
コントリビューションは歓迎します!バグ報告や機能提案は、GitHub の Issue を通じて行ってください。プルリクエストを送信する前に、以下のことを確認してください:
- テストが追加されていること
- コードスタイルが守られていること(
composer run-script analyzeを実行) - すべてのテストが通ること(
composer run-script testを実行)