Learn
Flight について学ぶ
Flight は、速く、シンプルで、拡張可能な PHP フレームワークです。非常に多用途で、どんな種類のウェブアプリケーションでも構築できます。
シンプルさを念頭に置いて設計されており、理解しやすく使いやすい方法で書かれています。
🚀 AI & Developer Experience with Flight
Flight は、速くてシンプルなだけでなく、現代の AI ツールを使ってよりスマートにコードを書いて生産性を高めるように設計されています。AI 駆動のコーディングアシスタントを使ったり、反復的なタスクを自動化したり、生産性を向上させたい場合、Flight の軽量なフットプリントとストレートな構造は、最新の開発体験と AI ワークフローを一緒に使うのに最適です。
- AI-Enhanced Coding: Flight は AI コーディングアシスタントとよく連携し、足場を構築したり、リファクタリングしたり、機能をこれまでより速く構築できます。
- Faster Prototyping: AI ツールの助けを借りて、新しいアイデアを素早く立ち上げて繰り返し改善できます — Flight は邪魔になりません。
- Integrate AI APIs: AI サービスに接続したり、スマートな機能を追加したりしたい場合、Flight はそれを簡単に行えますが、AI の専門家でなくてもメリットを得られます。
始め方を気になりますか? Explore our AI & DevEx guide を探して、Flight を使用して速く賢く作業するためのヒント、ツール、実世界の例を学んでください!
重要なフレームワークの概念
Why a Framework?
フレームワークを使う理由についての短い記事です。フレームワークを使う前に、その利点を理解することが良い考えです。
また、@lubiana によって作成された素晴らしいチュートリアルがあります。Flight について詳しく説明していませんが、
このガイドはフレームワークの主要な概念とその利点を理解するのに役立ちます。
チュートリアルは here で見つかります。
Flight Compared to Other Frameworks
Laravel、Slim、Fat-Free、または Symfony などの他のフレームワークから Flight に移行する場合、このページは両者の違いを理解するのに役立ちます。
コアトピック
AI & Developer Experience
AI ツールと現代の開発ワークフローで Flight がどのように連携して、速くスマートにコードを書くかを学んでください。
Autoloading
アプリケーションで独自のクラスを自動ロードする方法を学んでください。
Routing
ウェブアプリケーションのルートを管理する方法を学んでください。これにはルートのグループ化、ルートパラメータ、およびミドルウェアが含まれます。
Middleware
アプリケーションでリクエストとレスポンスをフィルタリングするためにミドルウェアを使う方法を学んでください。
Requests
アプリケーションでリクエストとレスポンスを扱う方法を学んでください。
Responses
ユーザーにレスポンスを送信する方法を学んでください。
Events
アプリケーションにカスタムイベントを追加するためにイベントシステムを使う方法を学んでください。
HTML Templates
組み込みのビューエンジンを使って HTML テンプレートをレンダリングする方法を学んでください。
Security
一般的なセキュリティ脅威からアプリケーションを保護する方法を学んでください。
Configuration
アプリケーションのためにフレームワークを設定する方法を学んでください。
Extending Flight
独自のメソッドとクラスを追加してフレームワークを拡張する方法を学んでください。
Events and Filtering
イベントシステムを使ってメソッドや内部フレームワークメソッドにフックを追加する方法を学んでください。
Dependency Injection Container
アプリケーションの依存関係を管理するために依存性注入コンテナ (DIC) を使う方法を学んでください。
Framework API
フレームワークのコアメソッドについて学んでください。
Migrating to v3
後方互換性はほとんど維持されていますが、v2 から v3 に移行する際に知っておくべきいくつかの変更点があります。
Troubleshooting
Flight を使用する際に遭遇する可能性のある一般的な問題をトラブルシューティングするのに役立つページです。
Learn/stopping
停止
フレームワークをhalt
メソッドを呼び出すことでいつでも停止できます:
Flight::halt();
オプションでHTTP
ステータスコードとメッセージを指定することもできます:
Flight::halt(200, 'Be right back...');
halt
を呼び出すと、その時点までのレスポンスコンテンツが破棄されます。フレームワークを停止して現在のレスポンスを出力するには、stop
メソッドを使用してください:
Flight::stop();
Learn/errorhandling
エラー処理
エラーと例外
Flight によってすべてのエラーや例外がキャッチされ、error
メソッドに渡されます。
デフォルトの動作は、いくつかのエラー情報を含む汎用 HTTP 500 内部サーバーエラー
応答を送信することです。
独自のニーズに合わせてこの動作を上書きすることができます:
Flight::map('error', function (Throwable $error) {
// エラーを処理する
echo $error->getTraceAsString();
});
デフォルトでは、エラーはウェブサーバーに記録されません。これを有効にすることでログを取得できます:
Flight::set('flight.log_errors', true);
見つかりません
URL が見つからない場合、Flight は notFound
メソッドを呼び出します。デフォルトの動作は、簡単なメッセージを含む HTTP 404 Not Found
応答を送信することです。
独自のニーズに合わせてこの動作を上書きすることができます:
Flight::map('notFound', function () {
// 見つからない場合の処理
});
Learn/flight_vs_laravel
フライト vs ララベル
ララベルとは何ですか?
Laravel は、すべての機能が備わっており、素晴らしい開発者向けエコシステムを持つフル機能のフレームワークですが、パフォーマンスと複雑さにはコストがかかります。 Laravelの目標は、開発者が最高レベルの生産性を持ち、一般的なタスクを簡単に行えるようにすることです。 Laravelは、フル機能のエンタープライズ Web アプリケーションを構築しようとしている開発者にとって優れた選択肢です。 これには、パフォーマンスと複雑さの観点でいくつかのトレードオフが伴います。 Laravelの基本を学ぶことは簡単ですが、フレームワークを習得するには時間がかかるかもしれません。
開発者がしばしば問題を解決する唯一の方法はこれらのモジュールであるかのように感じるほど、Laravelモジュールが非常に多いですが、実際には別のライブラリを使用するか、独自のコードを書くこともできます。
フライトとの比較でのメリット
- Laravel には一般的な問題を解決するために使用できる開発者とモジュールの膨大なエコシステムがあります。
- Laravel にはデータベースとやり取りするために使用できる豊富な ORM があります。
- Laravel には、フレームワークを学ぶのに使用できる膨大な文書とチュートリアルがあります。
- Laravel には、アプリケーションを保護するために使用できる組み込みの認証システムがあります。
- Laravel には、フレームワークを学ぶのに使用できるポッドキャスト、カンファレンス、ミーティング、ビデオ、その他のリソースがあります。
- Laravel は、フル機能のエンタープライズ Web アプリケーションを構築しようとしている経験豊富な開発者向けです。
フライトとの比較でのデメリット
- Laravel は Flight よりも深いところで多くのことを行っています。 これはパフォーマンスの観点で大きなコストがかかります。詳細については、TechEmpower ベンチマーク を参照してください。
- Flight は、軽量で高速で使いやすい Web アプリケーションを構築しようとしている開発者向けです。
- Flight は、シンプリシティと使いやすさを重視しています。
- Flight の主な機能の1つは、後方互換性を維持するために最善を尽くすことです。 Laravel は主要バージョン間で多くの苦情を引き起こします。
- Flight は、初めてフレームワークの世界に足を踏み入れる開発者向けです。
- Flight には依存関係がなく、一方、Laravel には多くの依存関係があります。
- Flight はエンタープライズレベルのアプリケーションも行うことができますが、Laravel のように多くのぼいらプレートコードがないです。 開発者がものを整理し、よく構造化するためにはより多くのディシプリンが必要です。
- Flight は開発者にアプリケーションをより多くコントロールさせ、一方、Laravel は裏側にたくさんの魔法があり、これがイライラすることがあります。
Learn/migrating_to_v3
v3への移行
ほとんどの場合、下位互換性は維持されていますが、v2からv3に移行する際に注意すべき変更がいくつかあります。
出力バッファリングの動作(3.5.0)
出力バッファリングは、PHPスクリプトによって生成された出力がクライアントに送信される前にバッファー(PHP内部)に保存されるプロセスです。これにより、出力をクライアントに送信する前に変更できます。
MVCアプリケーションでは、コントローラーが「マネージャー」であり、ビューの動作を管理します。コントローラーの外部(またはFlightの場合、時々無名関数内)で生成された出力は、MVCパターンを壊します。この変更は、MVCパターンにより準拠し、フレームワークを予測可能かつ使いやすくするためです。
v2では、出力バッファリングは、自身の出力バッファーを一貫してクローズしていなかったため、ユニットテストやストリーミングが困難になることがありました。ほとんどのユーザーにとって、この変更は実際には影響しないかもしれません。ただし、コールバックやコントローラーの外部でコンテンツをエコーしている場合(例えば、フック内で)、問題が発生する可能性があります。フック内やフレームワークの実際の実行より前にコンテンツをエコーしても、過去には動作していたかもしれませんが、今後は動作しません。
問題が発生する可能性がある場所
// index.php
require 'vendor/autoload.php';
// just an example
define('START_TIME', microtime(true));
function hello() {
echo 'Hello World';
}
Flight::map('hello', 'hello');
Flight::after('hello', function(){
// this will actually be fine
echo '<p>This Hello World phrase was brought to you by the letter "H"</p>';
});
Flight::before('start', function(){
// things like this will cause an error
echo '<html><head><title>My Page</title></head><body>';
});
Flight::route('/', function(){
// this is actually just fine
echo 'Hello World';
// This should be just fine as well
Flight::hello();
});
Flight::after('start', function(){
// this will cause an error
echo '<div>Your page loaded in '.(microtime(true) - START_TIME).' seconds</div></body></html>';
});
v2のレンダリング動作を有効にする
古いコードを修正せずにv3で機能させるためにはどうすればよいですか? はい、できます! flight.v2.output_buffering
構成オプションをtrue
に設定することで、v2のレンダリング動作を有効にできます。これにより、古いレンダリング動作を継続して使用できますが、将来の修正が推奨されています。 フレームワークのv4では、これが削除されます。
// index.php
require 'vendor/autoload.php';
Flight::set('flight.v2.output_buffering', true);
Flight::before('start', function(){
// Now this will be just fine
echo '<html><head><title>My Page</title></head><body>';
});
// more code
ディスパッチャーの変更(3.7.0)
Dispatcher::invokeMethod()
、Dispatcher::execute()
などのDispatcher
の静的メソッドを直接呼び出している場合、Dispatcher
がよりオブジェクト指向に変換されたため、これらのメソッドを直接呼び出さないようにコードを更新する必要があります。 依存性注入コンテナをより簡単に使用できるようにDispatcher
が変更されました。 Dispatcherと同様のメソッドを呼び出す必要がある場合は、手動で$result = $class->$method(...$params);
またはcall_user_func_array()
のようなものを使用することができます。
halt()
stop()
redirect()
および error()
の変更(3.10.0)
3.10.0以前のデフォルト動作は、ヘッダーとレスポンスボディの両方をクリアすることでした。これは、レスポンスボディのみをクリアするように変更されました。ヘッダーもクリアする必要がある場合は、Flight::response()->clear()
を使用できます。
Learn/configuration
設定
set
メソッドを使用して、Flight の特定の動作をカスタマイズできます。
Flight::set('flight.log_errors', true);
利用可能な設定
以下は利用可能な設定の一覧です:
- flight.base_url
?string
- リクエストのベース URL をオーバーライドします。 (デフォルト: null) - flight.case_sensitive
bool
- URL の大文字と小文字を区別します。 (デフォルト: false) - flight.handle_errors
bool
- Flight がすべてのエラーを内部で処理できるようにします。 (デフォルト: true) - flight.log_errors
bool
- エラーをウェブサーバーのエラーログファイルに記録します。 (デフォルト: false) - flight.views.path
string
- ビューテンプレートファイルが含まれるディレクトリ。 (デフォルト: ./views) - flight.views.extension
string
- ビューテンプレートファイルの拡張子。 (デフォルト: .php) - flight.content_length
bool
-Content-Length
ヘッダーを設定します。 (デフォルト: true) - flight.v2.output_buffering
bool
- 旧バージョンの出力バッファリングを使用します。 v3 へのマイグレーション を参照してください。 (デフォルト: false)
ローダーの設定
_
をクラス名に含める場合の追加のローダーの設定があります。これにより、クラスを自動的に読み込むことができます。
// アンダースコアを使用したクラスのローディングを有効にする
// デフォルトは true
Loader::$v2ClassLoading = false;
変数
Flight を使用すると、アプリケーション内のどこからでも使用できるように変数を保存できます。
// 変数を保存する
Flight::set('id', 123);
// アプリケーション内の別の場所で
$id = Flight::get('id');
変数が設定されているかどうかを確認するには、次のようにします:
if (Flight::has('id')) {
// 何かを行う
}
次のようにして変数をクリアできます:
// id 変数をクリアする
Flight::clear('id');
// すべての変数をクリア
Flight::clear();
Flight は設定目的で変数も使用します。
Flight::set('flight.log_errors', true);
エラー処理
エラーと例外
すべてのエラーと例外は Flight によってキャッチされ、error
メソッドに渡されます。デフォルトの動作は、一般的な HTTP 500 Internal Server Error
応答を送信し、いくつかのエラー情報を含めることです。
独自のニーズに合わせてこの動作をオーバーライドできます:
Flight::map('error', function (Throwable $error) {
// エラーを処理する
echo $error->getTraceAsString();
});
デフォルトでは、エラーはウェブサーバーにログ記録されていません。これを有効にできます:
Flight::set('flight.log_errors', true);
見つからない場合
URL が見つからない場合、Flight は notFound
メソッドを呼び出します。デフォルトの動作は、簡単なメッセージを含む HTTP 404 Not Found
応答を送信することです。
独自のニーズに合わせてこの動作をオーバーライドできます:
Flight::map('notFound', function () {
// 見つからなかった時の処理
});
Learn/ai
AI と開発者体験 with Flight
Flight は、より速く、より賢く、摩擦を少なくして構築するのを助けるものです。特に、AI 駆動のツールや現代の開発ワークフローで作業する場合です。このページでは、Flight がプロジェクトを AI で強化しやすくする方法、そしてフレームワークとスケルトンプロジェクトに組み込まれた新しい AI ヘルパーの使い方を説明します。
AI-Ready by Default: The Skeleton Project
公式の flightphp/skeleton スターターには、以下の人気の AI コーディングアシスタントの指示と設定が含まれています:
- GitHub Copilot
- Cursor
- Windsurf
これらのツールは、プロジェクト固有の指示で事前に設定されているため、コードを書く際に最も関連性が高く、文脈を考慮した助けを得られます。つまり:
- AI アシスタントは、プロジェクトの目標、スタイル、要件を理解します
- すべての貢献者に対して一貫したガイダンスを提供します
- 文脈を説明する時間を減らし、構築する時間を増やします
なぜこれが重要ですか?
AI ツールがプロジェクトの意図と規約を知っている場合、機能のスキャフォールディング、コードのリファクタリング、一般的なミスの回避を助けてくれます。これにより、初日からあなた(とあなたのチーム)がより生産的になります。
New AI Commands in Flight Core
v3.16.0+
Flight core には、プロジェクトを設定し、AI で導くのに役立つ 2 つの強力な CLI コマンドが含まれています:
1. ai:init
— Connect to Your Favorite LLM Provider
このコマンドは、OpenAI、Grok、または Anthropic (Claude) などの LLM (Large Language Model) プロバイダーの資格情報を設定する手順を案内します。
Example:
php runway ai:init
プロバイダーの選択、API キーの入力、モデルの選択を求められます。これにより、プロジェクトを最新の AI サービスに簡単に接続できます—手動設定は不要です。
2. ai:generate-instructions
— Project-Aware AI Coding Instructions
このコマンドは、プロジェクト固有の指示を AI コーディングアシスタント用に作成または更新します。プロジェクトの用途、使用するデータベース、チームの規模など、いくつかの簡単な質問をします。その後、LLM プロバイダーを使用して、調整された指示を生成します。
指示がすでに存在する場合、提供した回答を反映して更新します。これらの指示は自動的に以下に書き込まれます:
.github/copilot-instructions.md
(for Github Copilot).cursor/rules/project-overview.mdc
(for Cursor).windsurfrules
(for Windsurf)
Example:
php runway ai:generate-instructions
なぜこれが役立つのですか?
最新のプロジェクト固有の指示があれば、AI ツールは次のようにできます:
- より良いコードの提案を提供します
- プロジェクトの独自のニーズを理解します
- 新しい貢献者のオンボーディングを速めます
- プロジェクトが進化するにつれて、摩擦と混乱を減らします
Not Just for Building AI Apps
AI 駆動の機能(例:チャットボット、スマート API、または統合)を構築するために Flight を使用することもできますが、真の強みは、開発者として AI ツールをより良く活用する点にあります。それは:
- 生産性を向上させる AI 支援コーディング
- チームを揃える 共有され、進化する指示
- 新しい貢献者のオンボーディングを容易にする
- 構築に集中し、ツールとの戦いを避ける
Learn More & Get Started
- See the Flight Skeleton for a ready-to-go, AI-friendly starter
- Check out the rest of the Flight documentation for tips on building fast, modern PHP apps
Learn/security
セキュリティ
セキュリティはウェブアプリケーションにとって非常に重要です。アプリケーションが安全で、ユーザーのデータが守られていることを確認したいでしょう。Flightは、ウェブアプリケーションのセキュリティを確保するための多くの機能を提供しています。
ヘッダー
HTTPヘッダーは、ウェブアプリケーションを保護する最も簡単な方法の1つです。ヘッダーを使用して、クリックジャッキング、XSS、およびその他の攻撃を防ぐことができます。これらのヘッダーをアプリケーションに追加する方法はいくつかあります。
ヘッダーのセキュリティを確認するための優れたウェブサイトは、securityheaders.com と observatory.mozilla.org です。
手動で追加
Flight\Response
オブジェクトのheader
メソッドを使用して、これらのヘッダーを手動で追加できます。
// クリックジャッキングを防ぐためにX-Frame-Optionsヘッダーを設定
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
// XSSを防ぐためにContent-Security-Policyヘッダーを設定
// 注意: このヘッダーは非常に複雑になることがあるので、
// アプリケーションのためにインターネット上の例を参照することをお勧めします
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
// XSSを防ぐためにX-XSS-Protectionヘッダーを設定
Flight::response()->header('X-XSS-Protection', '1; mode=block');
// MIMEスニッフィングを防ぐためにX-Content-Type-Optionsヘッダーを設定
Flight::response()->header('X-Content-Type-Options', 'nosniff');
// どれだけのリファラー情報が送信されるかを制御するためにReferrer-Policyヘッダーを設定
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
// HTTPSを強制するためにStrict-Transport-Securityヘッダーを設定
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// 使用できる機能とAPIを制御するためにPermissions-Policyヘッダーを設定
Flight::response()->header('Permissions-Policy', 'geolocation=()');
これらは、bootstrap.php
やindex.php
ファイルの先頭に追加できます。
フィルターとして追加
次のようなフィルター/フックで追加することもできます:
// フィルター内でヘッダーを追加
Flight::before('start', function() {
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
Flight::response()->header('X-XSS-Protection', '1; mode=block');
Flight::response()->header('X-Content-Type-Options', 'nosniff');
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
Flight::response()->header('Permissions-Policy', 'geolocation=()');
});
ミドルウェアとして追加
ミドルウェアクラスとして追加することもできます。これは、コードをクリーンで整理する良い方法です。
// app/middleware/SecurityHeadersMiddleware.php
namespace app\middleware;
class SecurityHeadersMiddleware
{
public function before(array $params): void
{
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
Flight::response()->header('X-XSS-Protection', '1; mode=block');
Flight::response()->header('X-Content-Type-Options', 'nosniff');
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
Flight::response()->header('Permissions-Policy', 'geolocation=()');
}
}
// index.php またはルートがある場所
// FYI、この空の文字列グループはすべてのルートに対するグローバルミドルウェアとして機能します。
// もちろん、同じことをして特定のルートにのみ追加することもできます。
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// その他のルート
}, [ new SecurityHeadersMiddleware() ]);
クロスサイトリクエストフォージェリ (CSRF)
クロスサイトリクエストフォージェリ (CSRF) は、悪意のあるウェブサイトがユーザーのブラウザを使用して、あなたのウェブサイトにリクエストを送信させる攻撃の一種です。これを使用して、ユーザーの知らないうちにあなたのウェブサイトでアクションを実行することができます。Flightは、組み込みのCSRF保護メカニズムを提供していませんが、ミドルウェアを使用して自分自身のものを簡単に実装できます。
セットアップ
まず、CSRFトークンを生成し、ユーザーのセッションに保存する必要があります。その後、このトークンをフォームで使用し、フォームが送信されるときに確認します。
// CSRFトークンを生成し、ユーザーのセッションに保存
// (Flightにセッションオブジェクトを作成してアタッチしたと仮定)
// 詳細についてはセッションのドキュメントを参照してください
Flight::register('session', \Ghostff\Session\Session::class);
// セッションあたり1つのトークンを生成する必要があります(複数のタブや同じユーザーのリクエストで機能するため)
if(Flight::session()->get('csrf_token') === null) {
Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) );
}
<!-- フォームにCSRFトークンを使用 -->
<form method="post">
<input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>">
<!-- 他のフォームフィールド -->
</form>
Latteを使用する
独自の関数を定義してCSRFトークンをLatteテンプレートに出力することもできます。
// CSRFトークンを出力するカスタム関数を設定
// 注意: ViewはLatteをビューエンジンとして構成されています
Flight::view()->addFunction('csrf', function() {
$csrfToken = Flight::session()->get('csrf_token');
return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
});
これで、Latteテンプレート内でcsrf()
関数を使用してCSRFトークンを出力できます。
<form method="post">
{csrf()}
<!-- 他のフォームフィールド -->
</form>
短くてシンプルですよね?
CSRFトークンの確認
イベントフィルターを使用してCSRFトークンを確認できます:
// このミドルウェアはリクエストがPOSTリクエストであるかをチェックし、
// それが正しければCSRFトークンが有効であるかを確認します
Flight::before('start', function() {
if(Flight::request()->method == 'POST') {
// フォームの値からCSRFトークンを取得
$token = Flight::request()->data->csrf_token;
if($token !== Flight::session()->get('csrf_token')) {
Flight::halt(403, '無効なCSRFトークン');
// あるいはJSONレスポンスのために
Flight::jsonHalt(['error' => '無効なCSRFトークン'], 403);
}
}
});
または、ミドルウェアクラスを使用することもできます:
// app/middleware/CsrfMiddleware.php
namespace app\middleware;
class CsrfMiddleware
{
public function before(array $params): void
{
if(Flight::request()->method == 'POST') {
$token = Flight::request()->data->csrf_token;
if($token !== Flight::session()->get('csrf_token')) {
Flight::halt(403, '無効なCSRFトークン');
}
}
}
}
// index.php またはルートがある場所
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// その他のルート
}, [ new CsrfMiddleware() ]);
クロスサイトスクリプティング (XSS)
クロスサイトスクリプティング (XSS) は、悪意のあるウェブサイトがあなたのウェブサイトにコードを挿入できるようになる攻撃の一種です。これらの機会のほとんどは、あなたのエンドユーザーが記入するフォームの値から来ます。ユーザーからの出力を決して信頼しないでください!彼らが世界最高のハッカーであると常に仮定してください。彼らはあなたのページに悪意のあるJavaScriptやHTMLを挿入できます。このコードは、ユーザーの情報を盗むために使用されたり、あなたのウェブサイトでアクションを実行したりできます。Flightのビュークラスを使用することで、出力を簡単にエスケープしてXSS攻撃を防ぐことができます。
// ユーザーが賢いと仮定してこの名前を使用しようとした場合
$name = '<script>alert("XSS")</script>';
// これは出力をエスケープします
Flight::view()->set('name', $name);
// これは出力されます: <script>alert("XSS")</script>
// もしあなたがビュークラスとして登録されたLatteのようなものを使用すると、
// それも自動的にエスケープされます。
Flight::view()->render('template', ['name' => $name]);
SQLインジェクション
SQLインジェクションは、悪意のあるユーザーがデータベースにSQLコードを注入できるようになる攻撃の一種です。これを使用して、データベースから情報を盗んだり、データベースでアクションを実行したりできます。再び、ユーザーからの入力を決して信頼しないでください!彼らが血を求めていると常に仮定してください。PDO
オブジェクトでプリペアドステートメントを使用することで、SQLインジェクションを防ぐことができます。
// Flight::db()があなたのPDOオブジェクトとして登録されていると仮定
$statement = Flight::db()->prepare('SELECT * FROM users WHERE username = :username');
$statement->execute([':username' => $username]);
$users = $statement->fetchAll();
// PdoWrapperクラスを使用すると、これを1行で簡単に行えます
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $username ]);
// ? プレースホルダーを使用するPDOオブジェクトでも同様のことができます
$statement = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);
// こんなことは絶対にしないと約束してください...
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE username = '{$username}' LIMIT 5");
// だってもし$username = "' OR 1=1; -- "; だったらどうするのでしょうか?
// クエリが構築された後は、次のようになります
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5
// 不思議に見えますが、これは有効なクエリで機能します。実際、
// これはすべてのユーザーを返す非常に一般的なSQLインジェクション攻撃です。
CORS
クロスオリジンリソース共有 (CORS) は、ウェブページ上の多くのリソース(フォント、JavaScriptなど)が、リソースの発信元ドメイン外の別のドメインからリクエストされることを可能にするメカニズムです。Flightは組み込み機能を提供していませんが、Flight::start()
メソッドが呼び出される前に実行するフックで簡単に処理できます。
// app/utils/CorsUtil.php
namespace app\utils;
class CorsUtil
{
public function set(array $params): void
{
$request = Flight::request();
$response = Flight::response();
if ($request->getVar('HTTP_ORIGIN') !== '') {
$this->allowOrigins();
$response->header('Access-Control-Allow-Credentials', 'true');
$response->header('Access-Control-Max-Age', '86400');
}
if ($request->method === 'OPTIONS') {
if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_METHOD') !== '') {
$response->header(
'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'
);
}
if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') !== '') {
$response->header(
"Access-Control-Allow-Headers",
$request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
);
}
$response->status(200);
$response->send();
exit;
}
}
private function allowOrigins(): void
{
// ここで許可するホストをカスタマイズします。
$allowed = [
'capacitor://localhost',
'ionic://localhost',
'http://localhost',
'http://localhost:4200',
'http://localhost:8080',
'http://localhost:8100',
];
$request = Flight::request();
if (in_array($request->getVar('HTTP_ORIGIN'), $allowed, true) === true) {
$response = Flight::response();
$response->header("Access-Control-Allow-Origin", $request->getVar('HTTP_ORIGIN'));
}
}
}
// index.php またはルートがある場所
$CorsUtil = new CorsUtil();
// これはstartが実行される前に実行される必要があります。
Flight::before('start', [ $CorsUtil, 'setupCors' ]);
エラーハンドリング
攻撃者に情報を漏らさないように、プロダクション環境では敏感なエラーの詳細を非表示にします。
// bootstrap.php または index.php で
// flightphp/skeletonでは、これは app/config/config.php にあります
$environment = ENVIRONMENT;
if ($environment === 'production') {
ini_set('display_errors', 0); // エラー表示を無効にする
ini_set('log_errors', 1); // エラーをログに記録する
ini_set('error_log', '/path/to/error.log');
}
// ルートやコントローラー内で
// コントロールされたエラー応答には Flight::halt() を使用
Flight::halt(403, 'アクセス拒否');
入力のサニタイズ
ユーザー入力を信頼しないでください。悪意のあるデータが入り込まないように、処理の前にサニタイズします。
// $_POST['input'] および $_POST['email'] を持つ$_POSTリクエストがあると仮定します
// 文字列入力をサニタイズ
$clean_input = filter_var(Flight::request()->data->input, FILTER_SANITIZE_STRING);
// メールをサニタイズ
$clean_email = filter_var(Flight::request()->data->email, FILTER_SANITIZE_EMAIL);
パスワードのハッシュ化
パスワードを安全に保存し、安全に確認します。PHPの組み込み関数を使用してください。
$password = Flight::request()->data->password;
// パスワードを保存する際にハッシュ化します(例: 登録時)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// パスワードを確認します(例: ログイン時)
if (password_verify($password, $stored_hash)) {
// パスワードが一致します
}
レート制限
キャッシュを使用してリクエスト率を制限し、ブルートフォース攻撃から保護します。
// flightphp/cache がインストールされ、登録されていると仮定します
// ミドルウェア内で flightphp/cache を使用
Flight::before('start', function() {
$cache = Flight::cache();
$ip = Flight::request()->ip;
$key = "rate_limit_{$ip}";
$attempts = (int) $cache->retrieve($key);
if ($attempts >= 10) {
Flight::halt(429, 'リクエストが多すぎます');
}
$cache->set($key, $attempts + 1, 60); // 60秒後にリセット
});
結論
セキュリティは非常に重要であり、ウェブアプリケーションが安全であることを確認することが重要です。Flightはウェブアプリケーションを保護するための多くの機能を提供しますが、常に警戒し、ユーザーのデータを守るためにできることをすべて行うことが重要です。最悪の事態を常に想定し、ユーザーからの入力を決して信頼しないでください。出力は常にエスケープし、SQLインジェクションを防ぐためにプリペアドステートメントを使用してください。CSRFやCORS攻撃からルートを保護するために、常にミドルウェアを使用してください。これらすべてを実行すれば、安全なウェブアプリケーションを構築する道のりが開けるでしょう。
Learn/overriding
オーバーライド
Flightは、コードを変更することなく、独自のニーズに合わせてデフォルトの機能をオーバーライドできるようにします。
たとえば、FlightがURLをルートにマッチさせられない場合、notFound
メソッドが呼び出され、一般的な HTTP 404
レスポンスが送信されます。この動作を以下のようにオーバーライドできます:
Flight::map('notFound', function() {
// カスタム404ページを表示
include 'errors/404.html';
});
Flightはまた、フレームワークのコアコンポーネントを置換することを許可します。 たとえば、デフォルトのRouterクラスを独自のカスタムクラスで置き換えることができます:
// カスタムクラスを登録
Flight::register('router', MyRouter::class);
// FlightがRouterインスタンスをロードするとき、あなたのクラスがロードされます
$myrouter = Flight::router();
map
やregister
などのフレームワークのメソッドはオーバーライドできません。これを試みるとエラーが発生します。
Learn/routing
ルーティング
注: ルーティングについてもっと理解したいですか?より詳細な説明については、"なぜフレームワーク?"ページをチェックしてください。
Flightでの基本的なルーティングは、URLパターンをコールバック関数またはクラスとメソッドの配列と照合することによって行われます。
Flight::route('/', function(){
echo 'こんにちは世界!';
});
ルートは定義された順序で一致します。リクエストに一致する最初のルートが呼び出されます。
コールバック/関数
コールバックは呼び出し可能な任意のオブジェクトである必要があります。つまり、通常の関数を使用することができます:
function hello() {
echo 'こんにちは世界!';
}
Flight::route('/', 'hello');
クラス
クラスの静的メソッドを使用することもできます:
class Greeting {
public static function hello() {
echo 'こんにちは世界!';
}
}
Flight::route('/', [ 'Greeting','hello' ]);
または、最初にオブジェクトを作成し、メソッドを呼び出すこともできます:
// Greeting.php
class Greeting
{
public function __construct() {
$this->name = 'ジョン・ドー';
}
public function hello() {
echo "こんにちは、{$this->name}!";
}
}
// index.php
$greeting = new Greeting();
Flight::route('/', [ $greeting, 'hello' ]);
// オブジェクトを最初に作成せずにこれを行うこともできます
// 注: 引数はコンストラクタに注入されません
Flight::route('/', [ 'Greeting', 'hello' ]);
// さらに短い構文を使用できます
Flight::route('/', 'Greeting->hello');
// または
Flight::route('/', Greeting::class.'->hello');
DIC(依存性注入コンテナ)による依存性注入
コンテナを介して依存性注入を使用したい場合(PSR-11、PHP-DI、Diceなど)、利用できるルートの種類は、オブジェクトを自分で直接作成し、コンテナを使用してオブジェクトを作成するか、呼び出すクラスとメソッドを文字列で定義する必要があります。詳細については、依存性注入ページをご覧ください。
以下は簡単な例です:
use flight\database\PdoWrapper;
// Greeting.php
class Greeting
{
protected PdoWrapper $pdoWrapper;
public function __construct(PdoWrapper $pdoWrapper) {
$this->pdoWrapper = $pdoWrapper;
}
public function hello(int $id) {
// $this->pdoWrapperで何かをする
$name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
echo "こんにちは、世界!私の名前は{$name}です!";
}
}
// index.php
// 必要なパラメータでコンテナを設定
// PSR-11に関する詳細情報は依存性注入ページを参照してください
$dice = new \Dice\Dice();
// '$dice ='で変数を再割り当てすることを忘れないでください!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
'shared' => true,
'constructParams' => [
'mysql:host=localhost;dbname=test',
'root',
'password'
]
]);
// コンテナハンドラを登録
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// 通常のようにルートを設定
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// または
Flight::route('/hello/@id', 'Greeting->hello');
// または
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
メソッドルーティング
デフォルトでは、ルートパターンはすべてのリクエストメソッドに対して一致します。特定のメソッドに応答するには、URLの前に識別子を置きます。
Flight::route('GET /', function () {
echo 'GETリクエストを受け取りました。';
});
Flight::route('POST /', function () {
echo 'POSTリクエストを受け取りました。';
});
// Flight::get()はルートには使用できません。これは変数を取得するためのメソッドであり、ルートを作成するものではありません。
// Flight::post('/', function() { /* コード */ });
// Flight::patch('/', function() { /* コード */ });
// Flight::put('/', function() { /* コード */ });
// Flight::delete('/', function() { /* コード */ });
複数のメソッドを単一のコールバックにマッピングすることもできます。|
区切りを使用します:
Flight::route('GET|POST /', function () {
echo 'GETまたはPOSTリクエストのいずれかを受け取りました。';
});
さらに、いくつかのヘルパーメソッドを使用するためのRouterオブジェクトを取得できます:
$router = Flight::router();
// すべてのメソッドをマッピングする
$router->map('/', function() {
echo 'こんにちは世界!';
});
// GETリクエスト
$router->get('/users', function() {
echo 'ユーザー';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();
正規表現
ルートに正規表現を使用できます:
Flight::route('/user/[0-9]+', function () {
// これは/user/1234と一致します
});
この方法は利用可能ですが、名前付きパラメータや正規表現を使った名前付きパラメータを使用することをお勧めします。なぜなら、それらはより可読性があり、メンテナンスが容易だからです。
名前付きパラメータ
ルートで名前付きパラメータを指定することができ、コールバック関数に渡されます。これはルートの可読性のためだけのものです。それ以外に特に注意が必要です。
Flight::route('/@name/@id', function (string $name, string $id) {
echo "こんにちは、$name ($id)!";
});
名前付きパラメータに正規表現を含めることもできます。:
区切りを使用します:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// これは/bob/123と一致します
// しかし/bob/12345とは一致しません
});
注: 一致する正規表現グループ
()
と位置パラメータはサポートされていません。 :'(
重要な注意点
上記の例では@name
が変数$name
に直接結びついているように見えますが、実際にはそうではありません。コールバック関数のパラメータの順序が、それに渡されるものを決定します。したがって、コールバック関数のパラメータの順序を切り替えると、変数も切り替わります。以下はその例です:
Flight::route('/@name/@id', function (string $id, string $name) {
echo "こんにちは、$name ($id)!";
});
次のURLでアクセスした場合:/bob/123
、出力はこんにちは、123 (bob)!
になります。
ルートとコールバック関数を設定する際には注意してください。
省略可能なパラメータ
一致する省略可能な名前付きパラメータを指定するには、セグメントを括弧で囲みます。
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// 次のURLに一致します:
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);
一致しない省略可能なパラメータはNULL
として渡されます。
ワイルドカード
一致は個々のURLセグメントのみに行われます。複数のセグメントを一致させたい場合は、*
ワイルドカードを使用できます。
Flight::route('/blog/*', function () {
// これは/blog/2000/02/01に一致します
});
すべてのリクエストを単一のコールバックにルーティングするには、次のようにします:
Flight::route('*', function () {
// 何かをする
});
パススルー
コールバック関数からtrue
を返すことで、次の一致するルートに実行を渡すことができます。
Flight::route('/user/@name', function (string $name) {
// 条件を確認する
if ($name !== "Bob") {
// 次のルートに続行
return true;
}
});
Flight::route('/user/*', function () {
// これは呼び出されます
});
ルートエイリアス
ルートにエイリアスを割り当てることで、後でコード内でURLを動的に生成できるようになります(例えば、テンプレートのように)。
Flight::route('/users/@id', function($id) { echo 'ユーザー:'.$id; }, false, 'user_view');
// 後でコード内のどこかで
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5'を返します
これは、URLが変更される場合に特に便利です。上記の例では、ユーザーが/admin/users/@id
に移動したとします。
エイリアスを使用しているため、エイリアスを参照する場所すべてを変更する必要はありません。エイリアスは現在/admin/users/5
を返します。
ルートエイリアスはグループ内でも機能します:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'ユーザー:'.$id; }, false, 'user_view');
});
// 後でコード内のどこかで
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5'を返します
ルート情報
一致するルート情報を調査したい場合は、ルートメソッドの第3パラメータにtrue
を渡すことで、ルートオブジェクトをコールバックに渡すようリクエストできます。ルートオブジェクトは、常にコールバック関数に渡される最後のパラメータになります。
Flight::route('/', function(\flight\net\Route $route) {
// 一致したHTTPメソッドの配列
$route->methods;
// 名前付きパラメータの配列
$route->params;
// 一致する正規表現
$route->regex;
// URLパターンで使用された'*'の内容を含みます
$route->splat;
// URLパスを示します....本当に必要な場合
$route->pattern;
// このルートに割り当てられたミドルウェアを示します
$route->middleware;
// このルートに割り当てられたエイリアスを示します
$route->alias;
}, true);
ルートグループ
関連するルートをグループ化したい場合があります(例えば/api/v1
など)。これを行うにはgroup
メソッドを使用します:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// /api/v1/usersに一致します
});
Flight::route('/posts', function () {
// /api/v1/postsに一致します
});
});
グループのグループを入れ子にすることもできます:
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get()は変数を取得し、それはルートを設定しない!下のオブジェクトコンテキストを参照してください
Flight::route('GET /users', function () {
// GET /api/v1/usersに一致します
});
Flight::post('/posts', function () {
// POST /api/v1/postsに一致します
});
Flight::put('/posts/1', function () {
// PUT /api/v1/postsに一致します
});
});
Flight::group('/v2', function () {
// Flight::get()は変数を取得し、それはルートを設定しない!下のオブジェクトコンテキストを参照してください
Flight::route('GET /users', function () {
// GET /api/v2/usersに一致します
});
});
});
オブジェクトコンテキストを使用したグループ化
次のようにEngine
オブジェクトを使用してルートグループを使用することもできます:
$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {
// $router変数を使用
$router->get('/users', function () {
// GET /api/v1/usersに一致します
});
$router->post('/posts', function () {
// POST /api/v1/postsに一致します
});
});
リソースルーティング
resource
メソッドを使用して、リソース用のルートセットを作成できます。これにより、RESTful規約に従ったリソースのためのルートセットが作成されます。
リソースを作成するには、次のようにします:
Flight::resource('/users', UsersController::class);
裏では、次のルートが作成されます:
[
'index' => 'GET ',
'create' => 'GET /create',
'store' => 'POST ',
'show' => 'GET /@id',
'edit' => 'GET /@id/edit',
'update' => 'PUT /@id',
'destroy' => 'DELETE /@id'
]
そして、あなたのコントローラーは次のようになります:
class UsersController
{
public function index(): void
{
}
public function show(string $id): void
{
}
public function create(): void
{
}
public function store(): void
{
}
public function edit(string $id): void
{
}
public function update(string $id): void
{
}
public function destroy(string $id): void
{
}
}
注: 新しく追加されたルートは、
php runway routes
を実行してrunway
で表示できます。
リソースルートのカスタマイズ
リソースルートを構成するいくつかのオプションがあります。
エイリアスベース
aliasBase
を構成できます。デフォルトでは、エイリアスは指定されたURLの最後の部分です。
例えば、/users/
はusers
のaliasBase
になります。これらのルートが作成されると、エイリアスはusers.index
、users.create
などとなります。エイリアスを変更する場合は、aliasBase
を希望する値に設定してください。
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
除外とのみ
only
およびexcept
オプションを使用して、作成したいルートを指定することもできます。
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
これらは基本的にホワイトリストおよびブラックリストオプションであり、作成したいルートを指定できます。
ミドルウェア
resource
メソッドで作成された各ルートで実行されるミドルウェアを指定することもできます。
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
ストリーミング
streamWithHeaders()
メソッドを使用して、クライアントにレスポンスをストリーミングできるようになりました。これは、大きなファイルや長時間実行されるプロセス、大きなレスポンスを生成するのに便利です。
ストリーミングルートの処理は、通常のルートとは少し異なります。
注: ストリーミングレスポンスは、
flight.v2.output_buffering
がfalseに設定されている場合のみ利用可能です。
手動ヘッダーでのストリーム
ルートでstream()
メソッドを使用して、クライアントにレスポンスをストリーミングできます。これを行う場合、出力する前に手動ですべてのメソッドを設定する必要があります。
これはheader()
PHP関数またはFlight::response()->setRealHeader()
メソッドを使用して行います。
Flight::route('/@filename', function($filename) {
// 明らかに、パスやその他のものをサニタイズする必要があります。
$fileNameSafe = basename($filename);
// ルートが実行された後にここに追加のヘッダーを設定する場合、
// 出力される前にすべて定義する必要があります。
// それらはすべてheader()関数への生の呼び出しか、
// Flight::response()->setRealHeader()への呼び出しである必要があります。
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// または
Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$fileNameSafe.'"');
$fileData = file_get_contents('/some/path/to/files/'.$fileNameSafe);
// エラー処理とその他の処理
if(empty($fileData)) {
Flight::halt(404, 'ファイルが見つかりません');
}
// 必要に応じて手動でコンテンツ長を設定
header('Content-Length: '.filesize($filename));
// データをクライアントにストリーミング
echo $fileData;
// これが魔法の行です
})->stream();
ヘッダーでのストリーム
ストリーミングを開始する前にヘッダーを設定するために、streamWithHeaders()
メソッドを使用することもできます。
Flight::route('/stream-users', function() {
// ここに追加する任意のヘッダーを追加できます
// header()またはFlight::response()->setRealHeader()を使用する必要があります
// どのようにデータを取得するかは、あくまで例として...
$users_stmt = Flight::db()->query("SELECT id, first_name, last_name FROM users");
echo '{';
$user_count = count($users);
while($user = $users_stmt->fetch(PDO::FETCH_ASSOC)) {
echo json_encode($user);
if(--$user_count > 0) {
echo ',';
}
// これはデータをクライアントに送信するために必要です
ob_flush();
}
echo '}';
// ストリーミングを開始する前にヘッダーを設定する方法です。
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// オプションのステータスコード、デフォルトは200
'status' => 200
]);
Learn/flight_vs_symfony
フライト対シンフォニー
シンフォニーとは?
Symfony は、再利用可能な PHP コンポーネントと、Web プロジェクト用の PHP フレームワークです。
最高の PHP アプリケーションが構築される標準基盤。独自のアプリケーションに使用できる 50 個のスタンドアロンコンポーネントのいずれかを選択してください。
PHP Web アプリケーションの作成とメンテナンスをスピードアップします。反復的なコーディングタスクを終了し、コードを制御する力を享受します。
フライトとの比較での長所
- Symfony には一般的な問題を解決するために使用できる 膨大なエコシステム の開発者とモジュールがあります。
- Symfony にはデータベースとのやり取りに使用できる 充実した ORM (Doctrine) があります。
- Symfony には、フレームワークを学ぶために使用できる豊富なドキュメントやチュートリアルがあります。
- Symfony には、フレームワークを学ぶために使用できるポッドキャスト、会議、ミーティング、ビデオ、その他のリソースがあります。
- Symfony は、完全な機能を備えたエンタープライズ Web アプリケーションを構築しようとしている経験豊富な開発者向けです。
フライトとの比較での短所
- Symfony には Flight よりもはるかに多くのことが暗に行われています。これはパフォーマンスの面で 劇的な コストがかかります。詳細は TechEmpower benchmarks を参照してください。
- Flight は、軽量で高速かつ使いやすい Web アプリケーションを構築したい開発者向けです。
- Flight は単純さと使いやすさを重視しています。
- Flight の中心的な機能の1つは、後方互換性を維持するように最善を尽くすことです。
- Flight には依存関係がなく、一方 Symfony には多くの依存関係 があります。
- Flight は初めてフレームワークの世界に進む開発者向けです。
- Flight はエンタープライズレベルのアプリケーションもできますが、Symfony ほどの例やチュートリアルがないため、開発者側が整理された構造を維持するためにはより多くの努力が必要です。
- Flight は開発者にアプリケーション上の制御をより多く与え、一方 Symfony は裏でいくらかの魔法を行う可能性があります。
Learn/flight_vs_another_framework
他のフレームワークとのFlightの比較
もし、Laravel、Slim、Fat-Free、あるいはSymfonyのような他のフレームワークからFlightに移行している場合、このページは両者の違いを理解するのに役立ちます。
Laravel
Laravelはベルと笛がすべて揃った充実したフレームワークで、驚くべき開発者中心のエコシステムを持っていますが、パフォーマンスと複雑さと引き換えになります。
Slim
SlimはFlightに似たマイクロフレームワークです。軽量で使いやすく設計されていますが、Flightよりも少し複雑になることがあります。
Fat-Free
Fat-Freeはより小さなパッケージで提供されるフルスタックフレームワークです。ツールはすべてそろっていますが、いくつかのプロジェクトをより複雑にするデータアーキテクチャを持っています。
Symfony
Symfonyはモジュール式のエンタープライズレベルのフレームワークであり、柔軟性と拡張性を備えています。より小さなプロジェクトや新人開発者にとって、Symfonyは少し抵抗があるかもしれません。
Learn/variables
変数
Flight は、変数を保存してアプリケーション内のどこでも使用できるようにします。
// あなたの変数を保存する
Flight::set('id', 123);
// アプリケーションの別の場所で
$id = Flight::get('id');
変数が設定されているかどうかを確認するには、次のようにします。
if (Flight::has('id')) {
// 何かを実行
}
変数をクリアするには、次のようにします。
// id 変数をクリア
Flight::clear('id');
// すべての変数をクリア
Flight::clear();
Flight は設定目的で変数も使用します。
Flight::set('flight.log_errors', true);
Learn/dependency_injection_container
依存性の注入コンテナ
はじめに
依存性の注入コンテナ (DIC) は、アプリケーションの依存関係を管理する強力なツールであります。これは、現代のPHPフレームワークで重要な概念であり、オブジェクトのインスタンス化と構成を管理するために使用されます。DIC ライブラリの例には次のものがあります: Dice, Pimple, PHP-DI, および league/container。
DIC とは、クラスを中央集権的に作成および管理できるという洒落た方法です。これは、同じオブジェクトを複数のクラス (たとえば、コントローラー) に渡す必要がある場合に役立ちます。簡単な例は、この概念を理解するのに役立つでしょう。
基本的な例
昔は以下のように行っていたかもしれません:
// 以前の方法
以上のコードからわかるように、新しい PDO
オブジェクトを作成して UserController
クラスに渡しています。これは小規模なアプリケーションには適していますが、アプリケーションが成長すると、同じ PDO
オブジェクトを複数箇所で作成していることがわかります。ここで DIC が役立ちます。
こちらは (Dice を使用した) DIC を使用して同じ例を示したものです:
// DIC を使用した同じ例
もしかすると、この例には多くの余分なコードが追加されたと思っているかもしれません。その魔法は、PDO
オブジェクトが必要な別のコントローラーを持っている場合に来ます。
// 他のコントローラーが PDO オブジェクトが必要な場合
DIC を利用することの追加メリットは、ユニットテストがはるかに容易になることです。モックオブジェクトを作成してクラスに渡すことができます。これは、アプリケーションのテストを書く際に非常に有益です!
PSR-11
Flight は PSR-11 に準拠したコンテナも利用できます。これは、PSR-11 インターフェースを実装した任意のコンテナを使用できることを意味します。以下は、League の PSR-11 コンテナを使用した例です:
// PSR-11 コンテナを使用した例
この Dice の例よりも少し冗長かもしれませんが、同じ利点を持って問題なく機能します!
カスタム DIC ハンドラ
独自の DIC ハンドラも作成することができます。これは、PSR-11 ではないカスタムコンテナを使用したい場合に役立ちます。これを行う方法についての詳細については、基本的な例を参照してください。
さらに、Flight の使用時に生活をより簡単にするためのいくつかの便利なデフォルトがあります。
エンジンインスタンス
コントローラー/ミドルウェアで Engine
インスタンスを使用する場合、以下のように構成できます:
// エンジンインスタンスを使用する方法
他のクラスの追加
コンテナに追加したい他のクラスがある場合、Dice を使用すると自動的に解決されます。以下は例です:
// 他のクラスの追加
Learn/middleware
ルートミドルウェア
Flightはルートおよびグループルートのミドルウェアをサポートしています。ミドルウェアは、ルートコールバックの前(または後)に実行される関数です。これは、コード内にAPI認証チェックを追加したり、ユーザーがルートにアクセスする権限を持っていることを検証するのに便利な方法です。
基本的なミドルウェア
基本的な例を以下に示します:
// 無名関数のみを指定する場合、ルートコールバックの前に実行されます。
// 「after」ミドルウェア関数はクラスを除いて存在しません(以下を参照)
Flight::route('/path', function() { echo 'Here I am!'; })->addMiddleware(function() {
echo 'Middleware first!';
});
Flight::start();
// これにより、「Middleware first! Here I am!」と表示されます。
ミドルウェアについて重要な注意事項がいくつかありますので、使用する前に認識しておく必要があります:
- ミドルウェア関数はルートに追加された順に実行されます。実行は、Slim Frameworkがこれをどのように処理するのかに似ています。
- 「before」は追加された順に実行され、「after」は逆の順で実行されます。
- ミドルウェア関数がfalseを返すと、すべての実行が停止され、403 Forbiddenエラーがスローされます。これをよりスムーズに処理したい場合は、
Flight::redirect()
などを使用すると良いでしょう。 - ルートからパラメーターが必要な場合、それらは1つの配列としてミドルウェア関数に渡されます(
function($params) { ... }
またはpublic function before($params) {}
)。これは、パラメーターをグループ化し、その中のいくつかのグループで、パラメーターが実際に異なる順序で表示される場合があるためです。これにより、位置ではなく名前でアクセスできます。 - ミドルウェアの名前のみを渡すと、依存性注入コンテナによって自動的に実行され、必要なパラメーターでミドルウェアが実行されます。依存性注入コンテナが登録されていない場合は、
__construct()
にflight\Engine
インスタンスが渡されます。
ミドルウェアクラス
ミドルウェアはクラスとしても登録できます。"after"機能が必要な場合は、必ずクラスを使用する必要があります。
class MyMiddleware {
public function before($params) {
echo 'Middleware first!';
}
public function after($params) {
echo 'Middleware last!';
}
}
$MyMiddleware = new MyMiddleware();
Flight::route('/path', function() { echo 'Here I am! '; })->addMiddleware($MyMiddleware); // または ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);
Flight::start();
// これにより、「Middleware first! Here I am! Middleware last!」が表示されます。
ミドルウェアエラーの処理
認証ミドルウェアがあるとして、認証されていない場合にユーザーをログインページにリダイレクトしたいとします。その場合、次のオプションがいくつかあります:
- ミドルウェア関数からfalseを返すと、Flightは自動的に403 Forbiddenエラーを返しますが、カスタマイズはできません。
Flight::redirect()
を使用してユーザーをログインページにリダイレクトできます。- ミドルウェア内でカスタムエラーを作成し、ルートの実行を停止できます。
基本的な例
次に、単純なfalseを返す例を示します:
class MyMiddleware {
public function before($params) {
if (isset($_SESSION['user']) === false) {
return false;
}
// trueであるため、すべてが進行し続けます
}
}
リダイレクトの例
ユーザーをログインページにリダイレクトする例は次のとおりです:
class MyMiddleware {
public function before($params) {
if (isset($_SESSION['user']) === false) {
Flight::redirect('/login');
exit;
}
}
}
カスタムエラーの例
APIを構築しているため、JSONエラーをスローする必要があるとしましょう。これは以下のように行えます:
class MyMiddleware {
public function before($params) {
$authorization = Flight::request()->headers['Authorization'];
if(empty($authorization)) {
Flight::jsonHalt(['error' => 'You must be logged in to access this page.'], 403);
// または
Flight::json(['error' => 'You must be logged in to access this page.'], 403);
exit;
// または
Flight::halt(403, json_encode(['error' => 'You must be logged in to access this page.']);
}
}
}
ミドルウェアのグループ化
ルートグループを追加し、そのグループ内のすべてのルートに同じミドルウェアを適用できます。これは、例えばヘッダーのAPIキーをチェックするために、多くのルートをグループ化する必要がある場合に便利です。
// グループメソッドの最後に追加
Flight::group('/api', function() {
// この「空」に見えるルートは実際には/apiに一致します
Flight::route('', function() { echo 'api'; }, false, 'api');
// これは/api/usersに一致します
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// これは/api/users/1234に一致します
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);
すべてのルートにグローバルミドルウェアを適用する場合は、次のようにして「空の」グループを追加できます:
// グループメソッドの最後に追加
Flight::group('', function() {
// これは依然として/usersです
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// そしてこれは依然として/users/1234です
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);
Learn/filtering
フィルタリング
Flightは、メソッドが呼び出される前後にフィルタリングを行うことができます。覚える必要のある事前定義されたフックはありません。デフォルトのフレームワークメソッドやマップしたカスタムメソッドのいずれに対してもフィルタリングを行うことができます。
フィルタ関数は次のようになります:
function (array &$params, string &$output): bool {
// フィルタコード
}
渡された変数を使用して、入力パラメータや出力を操作することができます。
メソッドの前にフィルタを実行するには、次のようにします:
Flight::before('start', function (array &$params, string &$output): bool {
// 何かを行う
});
メソッドの後にフィルタを実行するには、次のようにします:
Flight::after('start', function (array &$params, string &$output): bool {
// 何かを行う
});
任意のメソッドに複数のフィルタを追加することができます。宣言された順序通りに呼び出されます。
以下はフィルタリングプロセスの例です:
// カスタムメソッドをマップ
Flight::map('hello', function (string $name) {
return "Hello, $name!";
});
// フィルタを追加
Flight::before('hello', function (array &$params, string &$output): bool {
// パラメータを操作する
$params[0] = 'Fred';
return true;
});
// フィルタを追加
Flight::after('hello', function (array &$params, string &$output): bool {
// 出力を操作する
$output .= " Have a nice day!";
return true;
});
// カスタムメソッドを呼び出す
echo Flight::hello('Bob');
これにより次のように表示されます:
Hello Fred! Have a nice day!
複数のフィルタを定義している場合は、フィルタ関数のいずれかで false
を返すことで、チェーンを中断することができます:
Flight::before('start', function (array &$params, string &$output): bool {
echo 'one';
return true;
});
Flight::before('start', function (array &$params, string &$output): bool {
echo 'two';
// これによりチェーンが終了します
return false;
});
// これは呼び出されません
Flight::before('start', function (array &$params, string &$output): bool {
echo 'three';
return true;
});
map
や register
などのコアメソッドは、直接呼び出されて動的に呼び出されないため、フィルタリングすることはできません。
Learn/requests
リクエスト
FlightはHTTPリクエストを単一のオブジェクトにカプセル化し、 次のようにアクセスできます:
$request = Flight::request();
一般的な使用例
Webアプリケーションでリクエストを処理する際は、通常、ヘッダーを
取り出したり、$_GET
や$_POST
のパラメータを取得したり、あるいは
生のリクエストボディを取得したいと思うことでしょう。Flightはそれを
簡単に行うためのインターフェースを提供します。
クエリ文字列パラメータを取得する例は以下の通りです:
Flight::route('/search', function(){
$keyword = Flight::request()->query['keyword'];
echo "あなたが検索しているのは: $keyword";
// $keywordを使ってデータベースにクエリするか、何か他のことをする
});
POSTメソッドのフォームの例はこちらです:
Flight::route('POST /submit', function(){
$name = Flight::request()->data['name'];
$email = Flight::request()->data['email'];
echo "あなたが送信したのは: $name, $email";
// $nameと$emailを使ってデータベースに保存するか、何か他のことをする
});
リクエストオブジェクトのプロパティ
リクエストオブジェクトは以下のプロパティを提供します:
- body - 生のHTTPリクエストボディ
- url - リクエストされているURL
- base - URLの親サブディレクトリ
- method - リクエストメソッド (GET, POST, PUT, DELETE)
- referrer - リファラURL
- ip - クライアントのIPアドレス
- ajax - リクエストがAJAXリクエストかどうか
- scheme - サーバープロトコル (http, https)
- user_agent - ブラウザ情報
- type - コンテンツタイプ
- length - コンテンツの長さ
- query - クエリ文字列パラメータ
- data - ポストデータまたはJSONデータ
- cookies - クッキーデータ
- files - アップロードされたファイル
- secure - 接続が安全かどうか
- accept - HTTPのacceptパラメータ
- proxy_ip - クライアントのプロキシIPアドレス。
HTTP_CLIENT_IP
、HTTP_X_FORWARDED_FOR
、HTTP_X_FORWARDED
、HTTP_X_CLUSTER_CLIENT_IP
、HTTP_FORWARDED_FOR
、HTTP_FORWARDED
をその順で$_SERVER
配列からスキャンします。 - host - リクエストホスト名
query
、data
、cookies
、およびfiles
プロパティには
配列またはオブジェクトとしてアクセスできます。
したがって、クエリ文字列パラメータを取得するには、次のようにできます:
$id = Flight::request()->query['id'];
または、次のようにできます:
$id = Flight::request()->query->id;
生のリクエストボディ
例えばPUTリクエストを扱うときに生のHTTPリクエストボディを取得するには、
$body = Flight::request()->getBody();
JSON入力
application/json
タイプのリクエストでデータ{"id": 123}
を送信すると、
それはdata
プロパティから利用可能になります:
$id = Flight::request()->data->id;
$_GET
$_GET
配列にはquery
プロパティを介してアクセスできます:
$id = Flight::request()->query['id'];
$_POST
$_POST
配列にはdata
プロパティを介してアクセスできます:
$id = Flight::request()->data['id'];
$_COOKIE
$_COOKIE
配列にはcookies
プロパティを介してアクセスできます:
$myCookieValue = Flight::request()->cookies['myCookieName'];
$_SERVER
$_SERVER
配列にはgetVar()
メソッドを介してショートカットでアクセスできます:
$host = Flight::request()->getVar['HTTP_HOST'];
$_FILES
を介してアップロードされたファイルにアクセスする
files
プロパティを介してアップロードされたファイルにアクセスできます:
$uploadedFile = Flight::request()->files['myFile'];
ファイルアップロードの処理
フレームワークを使用してファイルアップロードを処理できます。基本的には リクエストからファイルデータを取り出し、それを新しい場所に移動することです。
Flight::route('POST /upload', function(){
// <input type="file" name="myFile">のような入力フィールドがあった場合
$uploadedFileData = Flight::request()->getUploadedFiles();
$uploadedFile = $uploadedFileData['myFile'];
$uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
});
複数のファイルがアップロードされている場合は、それらをループ処理できます:
Flight::route('POST /upload', function(){
// <input type="file" name="myFiles[]">のような入力フィールドがあった場合
$uploadedFiles = Flight::request()->getUploadedFiles()['myFiles'];
foreach ($uploadedFiles as $uploadedFile) {
$uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
}
});
セキュリティノート: ユーザー入力を常に検証し、サニタイズしてください。特にファイルアップロードを扱う場合は注意が必要です。許可する拡張子のタイプを必ず検証し、ファイルが実際にユーザーが主張するファイルタイプであることを確認するために「マジックバイト」も検証してください。これに役立つ記事、および、ライブラリがあります。
リクエストヘッダー
getHeader()
またはgetHeaders()
メソッドを使用してリクエストヘッダーにアクセスできます:
// おそらくAuthorizationヘッダーが必要な場合
$host = Flight::request()->getHeader('Authorization');
// または
$host = Flight::request()->header('Authorization');
// すべてのヘッダーを取得する必要がある場合
$headers = Flight::request()->getHeaders();
// または
$headers = Flight::request()->headers();
リクエストボディ
getBody()
メソッドを使用して生のリクエストボディにアクセスできます:
$body = Flight::request()->getBody();
リクエストメソッド
method
プロパティまたはgetMethod()
メソッドを使用してリクエストメソッドにアクセスできます:
$method = Flight::request()->method; // 実際にはgetMethod()を呼び出す
$method = Flight::request()->getMethod();
注意: getMethod()
メソッドは最初に$_SERVER['REQUEST_METHOD']
からメソッドを取得し、その後、存在する場合は$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']
によって上書きされるか、存在する場合は$_REQUEST['_method']
によって上書きされることがあります。
リクエストURL
URLの部分を組み合わせるためのいくつかのヘルパーメソッドがあります。
完全URL
getFullUrl()
メソッドを使用して完全なリクエストURLにアクセスできます:
$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar
ベースURL
getBaseUrl()
メソッドを使用してベースURLにアクセスできます:
$url = Flight::request()->getBaseUrl();
// 注意: トレーリングスラッシュはありません。
// https://example.com
クエリ解析
parseQuery()
メソッドにURLを渡すことで、クエリ文字列を連想配列に解析できます:
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']
Learn/frameworkmethods
フレームワークのメソッド
Flightは使いやすく理解しやすいように設計されています。以下はフレームワークの完全なメソッドセットです。 コアメソッドには、通常の静的メソッドであるコアメソッドと、フィルタリングやオーバーライドが可能なマップされたメソッドである拡張メソッドが含まれています。
コアメソッド
Flight::map(string $name, callable $callback, bool $pass_route = false) // カスタムフレームワークメソッドを作成します。
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // クラスをフレームワークメソッドに登録します。
Flight::before(string $name, callable $callback) // フレームワークメソッドの前にフィルタを追加します。
Flight::after(string $name, callable $callback) // フレームワークメソッドの後にフィルタを追加します。
Flight::path(string $path) // クラスの自動読み込み用のパスを追加します。
Flight::get(string $key) // 変数を取得します。
Flight::set(string $key, mixed $value) // 変数を設定します。
Flight::has(string $key) // 変数が設定されているかどうかを確認します。
Flight::clear(array|string $key = []) // 変数をクリアします。
Flight::init() // フレームワークをデフォルト設定に初期化します。
Flight::app() // アプリケーションオブジェクトのインスタンスを取得します。
拡張メソッド
Flight::start() // フレームワークを開始します。
Flight::stop() // フレームワークを停止してレスポンスを送信します。
Flight::halt(int $code = 200, string $message = '') // オプションのステータスコードとメッセージでフレームワークを停止します。
Flight::route(string $pattern, callable $callback, bool $pass_route = false) // URLパターンをコールバックにマップします。
Flight::group(string $pattern, callable $callback) // URLのグループを作成します。パターンは文字列でなければなりません。
Flight::redirect(string $url, int $code) // 別のURLにリダイレクトします。
Flight::render(string $file, array $data, ?string $key = null) // テンプレートファイルをレンダリングします。
Flight::error(Throwable $error) // HTTP 500レスポンスを送信します。
Flight::notFound() // HTTP 404レスポンスを送信します。
Flight::etag(string $id, string $type = 'string') // ETag HTTPキャッシュを実行します。
Flight::lastModified(int $time) // 最終変更日HTTPキャッシュを実行します。
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // JSONレスポンスを送信します。
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // JSONPレスポンスを送信します。
map
とregister
で追加された任意のカスタムメソッドもフィルタリングできます。
Learn/api
フレームワークAPIメソッド
Flightは使いやすく、理解しやすいように設計されています。以下はフレームワークの完全なメソッドセットです。これは、通常の静的メソッドであるコアメソッドと、フィルタリングやオーバーライドが可能なマッピングメソッドである拡張可能メソッドで構成されています。
コアメソッド
これらのメソッドはフレームワークのコアであり、オーバーライドすることはできません。
Flight::map(string $name, callable $callback, bool $pass_route = false) // カスタムフレームワークメソッドを作成します。
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // フレームワークメソッドにクラスを登録します。
Flight::unregister(string $name) // フレームワークメソッドからクラスを登録解除します。
Flight::before(string $name, callable $callback) // フレームワークメソッドの前にフィルタを追加します。
Flight::after(string $name, callable $callback) // フレームワークメソッドの後にフィルタを追加します。
Flight::path(string $path) // クラスのオートローディング用のパスを追加します。
Flight::get(string $key) // Flight::set()によって設定された変数を取得します。
Flight::set(string $key, mixed $value) // Flightエンジン内で変数を設定します。
Flight::has(string $key) // 変数が設定されているかどうかをチェックします。
Flight::clear(array|string $key = []) // 変数をクリアします。
Flight::init() // フレームワークをデフォルト設定で初期化します。
Flight::app() // アプリケーションオブジェクトのインスタンスを取得します。
Flight::request() // リクエストオブジェクトのインスタンスを取得します。
Flight::response() // レスポンスオブジェクトのインスタンスを取得します。
Flight::router() // ルーターオブジェクトのインスタンスを取得します。
Flight::view() // ビューオブジェクトのインスタンスを取得します。
拡張可能メソッド
Flight::start() // フレームワークを開始します。
Flight::stop() // フレームワークを停止し、レスポンスを送信します。
Flight::halt(int $code = 200, string $message = '') // オプションのステータスコードとメッセージとともにフレームワークを停止します。
Flight::route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // URLパターンをコールバックにマッピングします。
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // POSTリクエストURLパターンをコールバックにマッピングします。
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // PUTリクエストURLパターンをコールバックにマッピングします。
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // PATCHリクエストURLパターンをコールバックにマッピングします。
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // DELETEリクエストURLパターンをコールバックにマッピングします。
Flight::group(string $pattern, callable $callback) // URLのグルーピングを作成します。パターンは文字列でなければなりません。
Flight::getUrl(string $name, array $params = []) // ルートエイリアスに基づいてURLを生成します。
Flight::redirect(string $url, int $code) // 別のURLにリダイレクトします。
Flight::download(string $filePath) // ファイルをダウンロードします。
Flight::render(string $file, array $data, ?string $key = null) // テンプレートファイルをレンダリングします。
Flight::error(Throwable $error) // HTTP 500レスポンスを送信します。
Flight::notFound() // HTTP 404レスポンスを送信します。
Flight::etag(string $id, string $type = 'string') // ETag HTTPキャッシュを実行します。
Flight::lastModified(int $time) // 最終更新のHTTPキャッシュを実行します。
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // JSONレスポンスを送信します。
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // JSONPレスポンスを送信します。
Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // JSONレスポンスを送信し、フレームワークを停止します。
Flight::onEvent(string $event, callable $callback) // イベントリスナーを登録します。
Flight::triggerEvent(string $event, ...$args) // イベントをトリガーします。
map
およびregister
で追加された任意のカスタムメソッドもフィルタリングできます。これらのメソッドをマッピングする方法の例については、Flightを拡張ガイドを参照してください。
Learn/why_frameworks
フレームワークを使う理由
一部のプログラマーは、フレームワークの使用に熱烈に反対しています。フレームワークは膨大で、遅く、学習が困難だと主張しています。 彼らは、フレームワークは不要であり、それらなしでより良いコードを書くことができると述べています。 フレームワークを使用することのデメリットについては、いくつかの妥当なポイントがあります。 ただし、フレームワークを使用する利点もたくさんあります。
フレームワークを使用する理由
フレームワークを使用したいと思う理由のいくつかを以下に示します:
- 迅速な開発: フレームワークは多くの機能を提供します。これにより、Webアプリケーションをより迅速に構築できます。 フレームワークが必要な機能の多くを提供しているため、多くのコードを書く必要がありません。
- 一貫性: フレームワークは、作業方法を一貫させる方法を提供します。 これにより、コードの動作方法を理解しやすくなり、他の開発者がコードを理解しやすくなります。 開発者チームと一緒に作業している場合、スクリプトごとに異なる一貫性が失われる可能性があります。
- セキュリティ: フレームワークは、一般的なセキュリティ脅威からWebアプリケーションを保護するためのセキュリティ機能を提供します。 これは、フレームワークが多くの部分を処理してくれるため、セキュリティについてあまり心配する必要がなくなります。
- コミュニティ: フレームワークには、フレームワークに貢献する大規模な開発者コミュニティが存在します。 これにより、質問や問題が生じた場合に他の開発者からサポートを受けることができます。 また、フレームワークの使用方法を学ぶのに利用できるリソースが豊富にあることを意味します。
- ベストプラクティス: フレームワークは、最善の方法で構築されています。 これにより、フレームワークから学び、自分自身のコードでも同じベストプラクティスを使用できます。 これにより、より優れたプログラマーになるのに役立ちます。 時には自分の知らないことがあることがあり、それが最後にあなたを苦しめる可能性があります。
- 拡張性: フレームワークは拡張可能に設計されています。 これは、フレームワークに独自の機能を追加できることを意味します。 これにより、特定のニーズに合わせたWebアプリケーションを構築できます。
Flightはマイクロフレームワークです。 つまり、小さく軽量です。 LaravelやSymfonyのような大規模なフレームワークほどの機能は提供しません。 ただし、Webアプリケーションを構築するために必要な機能の多くを提供します。 また、学びやすく使用も容易です。 これにより、簡単かつ迅速にWebアプリケーションを構築するのに適しています。 フレームワークに新しい場合は、Flightは初心者に最適なフレームワークです。 フレームワークを使用する利点を学び、過度な複雑さで圧倒されることなく学習するのに役立ちます。 Flightの経験を積んだ後は、LaravelやSymfonyなどのより複雑なフレームワークに移ることがより簡単になります。 ただし、Flightでも成功した堅牢なアプリケーションを作成できます。
ルーティングとは?
ルーティングはFlightフレームワークの中核ですが、それは一体何でしょうか? ルーティングとは、URLを取得してコード内の特定の関数に一致させるプロセスです。
これにより、WebサイトをリクエストされたURLに基づいて異なる動作をさせることができます。 たとえば、ユーザーが/user/1234
を訪れたときにユーザープロフィールを表示したいが、/users
を訪れたときに全ユーザーのリストを表示したいとします。 これはすべてルーティングを通じて行われます。
以下のようになります:
- ユーザーがブラウザに移動し、
http://example.com/user/1234
を入力します。 - サーバーがリクエストを受信し、URLを確認してFlightアプリケーションコードに渡します。
- あなたのFlightコードに
Flight::route('/user/@id', [ 'UserController', 'viewUserProfile' ]);
のようなものがあるとします。 FlightアプリケーションコードはURLを確認し、定義したルートに一致することを確認し、次にそのルートに対して定義したコードを実行します。 - Flightのルーターはその後動作し、
UserController
クラス内のviewUserProfile($id)
メソッドを呼び出し、メソッド内の$id
引数として1234
を渡します。 viewUserProfile()
メソッド内のコードが実行され、指示した内容が実行されます。 ユーザープロフィールページのHTMLをエコーするか、これがRESTful APIの場合は、ユーザーの情報を含むJSON応答をエコーするかもしれません。- Flightはこれらを簡潔にまとめ、応答ヘッダーを生成し、ユーザーのブラウザに送信します。
- ユーザーは喜びに満たされ、自分自身に温かい抱擁をします!
そして、なぜ重要なの?
適切な中央集権的なルーターを持つことで、あなたの生活が劇的に簡単になる可能性があります! 最初はそれが見えにくいかもしれません。 以下は、そのいくつか理由です:
- 中央集権的なルーティング: すべてのルートを1つの場所に保管できます。 これにより、どのルートがあり、それが何を行うかが見やすくなります。 必要に応じてルートを変更する際も便利です。
- ルートパラメータ: ルートパラメータを使用して、ルートメソッドにデータを渡すことができます。 これはコードをクリーンかつ整理された状態に保つのに最適な方法です。
- ルートグループ: ルートをグループ化できます。 これはコードを整理し、一連のルートにミドルウェアを適用するために優れています。
- ルートのエイリアス設定: ルートにエイリアスを割り当てることができます。 これにより、URLを後で動的に生成できるようになります(たとえば、テンプレートの場合)。 例:コード内で
/user/1234
をハードコーディングする代わりに、user_view
を参照し、id
をパラメーターとして渡すことができます。 後でURLを/admin/user/1234
に変更する場合に非常に便利です。 ハードコーディングしたすべてのURLを変更する必要はなくなります。 - ルートミドルウェア: ルートにミドルウェアを追加できます。 ミドルウェアは、特定のユーザーが特定のルートやルートグループにアクセスできるようにするなど、アプリケーションに特定の振る舞いを追加するには非常に強力です。
多分、Webサイトを作成するためのスクリプトごとの方法に慣れているかもしれません。 index.php
というファイルがあり、URLを確認し、URLに基づいて特定の関数を実行します。 これもルーティングの一形態ですが、整理されていない上にすぐに手に負えなくなります。 Flightのルーティングシステムは、ルーティングを処理するより整理された強力な方法です。
これ?
// /user/view_profile.php?id=1234
if ($_GET['id']) {
$id = $_GET['id'];
viewUserProfile($id);
}
// /user/edit_profile.php?id=1234
if ($_GET['id']) {
$id = $_GET['id'];
editUserProfile($id);
}
// etc...
それともこちら?
// index.php
Flight::route('/user/@id', [ 'UserController', 'viewUserProfile' ]);
Flight::route('/user/@id/edit', [ 'UserController', 'editUserProfile' ]);
// In maybe your app/controllers/UserController.php
class UserController {
public function viewUserProfile($id) {
// do something
}
public function editUserProfile($id) {
// do something
}
}
中央集権的なルーティングシステムを使用する利点が見え始めたことを願っています。 長い目で見れば、管理や理解が容易になります!
リクエストとレスポンス
Flightはリクエストとレスポンスを処理するための簡単で簡単な方法を提供します。 これがWebフレームワークの中核です。 ユーザーのブラウザからのリクエストを受け取り、処理してからレスポンスを返すことで、Webアプリケーションを構築できます。 これにより、ユーザープロフィールを表示したり、ユーザーのログインを許可したり、新しいブログ投稿を許可したりするWebアプリケーションを構築できます。
リクエスト
リクエストは、ユーザーのブラウザがウェブサイトを訪れるときにサーバーに送信するものです。 このリクエストには、ユーザーが何をしたいかに関する情報が含まれます。 たとえば、ユーザーが訪れたいURLの情報、ユーザーがサーバーに送りたいデータ、サーバーから受け取りたいデータの種類などが含まれるかもしれません。 リクエストは読み取り専用です。 リクエストを変更することはできませんが、読み取ることはできます。
Flightはリクエストに関する情報にアクセスするための簡単な方法を提供します。 Flight::request()
メソッドを使用してリクエストに関する情報にアクセスできます。 このメソッドはリクエストに関する情報を含むRequest
オブジェクトを返します。 このオブジェクトを使用して、URL、メソッド、ユーザーがサーバーに送信したデータなどの情報にアクセスできます。
レスポンス
レスポンスとは、ユーザーのブラウザがウェブサイトを訪れるときにサーバーがユーザーのブラウザに送り返すものです。 このレスポンスには、サーバーが行いたいことに関する情報が含まれます。 たとえば、サーバーがユーザーに送信したいデータの種類、ユーザーから受け取りたいデータの種類、サーバーがユーザーのコンピュータに保存したいデータの種類などが含まれるかもしれません。
Flightはユーザーのブラウザにレスポンスを送信する簡単な方法を提供します。 Flight::response()
メソッドを使用してレスポンスを送信できます。 このメソッドは、Response
オブジェクトを引数として受け取り、そのレスポンスをユーザーのブラウザに送信します。 このオブジェクトを使用して、HTML、JSON、ファイルなど、ユーザーのブラウザにレスポンスを送信できます。 Flightはレスポンスの一部を自動生成して簡単にするお手伝いをしますが、最終的にはユーザーに送り返す内容を制御できます。
Learn/httpcaching
HTTP キャッシング
Flight は HTTP レベルのキャッシングを組み込みでサポートしています。キャッシングの条件を満たすと、Flight は HTTP 304 Not Modified
のレスポンスを返します。クライアントが同じリソースを次にリクエストするときは、ローカルにキャッシュされたバージョンを使用するよう促されます。
Last-Modified
lastModified
メソッドを使用して、UNIX タイムスタンプを渡すことでページが最後に変更された日時を設定できます。クライアントは最終変更日時の値が変更されるまで、キャッシュを引き続き使用します。
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo 'このコンテンツはキャッシュされます。';
});
ETag
ETag
キャッシングは Last-Modified
と似ていますが、リソースに任意の ID を指定できます:
Flight::route('/news', function () {
Flight::etag('my-unique-id');
echo 'このコンテンツはキャッシュされます。';
});
lastModified
または etag
のいずれかを呼び出すと、キャッシュの値が設定されてチェックされます。リクエスト間でキャッシュの値が同じ場合、Flight は直ちに HTTP 304
レスポンスを送信して処理を停止します。
Learn/responses
レスポンス
Flight はあなたのためにレスポンスヘッダーの一部を生成しますが、ユーザーに返すものの大部分はあなたが制御します。場合によっては、Response
オブジェクトに直接アクセスできますが、ほとんどの場合、Flight
インスタンスを使用してレスポンスを送信します。
基本的なレスポンスの送信
Flight は ob_start() を使用して出力をバッファリングします。これは、echo
または print
を使用してユーザーにレスポンスを送信でき、Flight がそれをキャプチャして適切なヘッダーでユーザーに返すことを意味します。
// これにより「Hello, World!」がユーザーのブラウザに送信されます
Flight::route('/', function() {
echo "Hello, World!";
});
// HTTP/1.1 200 OK
// Content-Type: text/html
//
// Hello, World!
代わりに、write()
メソッドを呼び出してボディに追加することも可能です。
// これにより「Hello, World!」がユーザーのブラウザに送信されます
Flight::route('/', function() {
// 冗長ですが、必要なときに仕事をすることがあります
Flight::response()->write("Hello, World!");
// この時点で設定したボディを取得したい場合
// このようにして取得できます
$body = Flight::response()->getBody();
});
ステータスコード
status
メソッドを使用してレスポンスのステータスコードを設定できます:
Flight::route('/@id', function($id) {
if($id == 123) {
Flight::response()->status(200);
echo "Hello, World!";
} else {
Flight::response()->status(403);
echo "Forbidden";
}
});
現在のステータスコードを取得したい場合は、引数なしで status
メソッドを使用できます:
Flight::response()->status(); // 200
レスポンスボディの設定
write
メソッドを使用してレスポンスボディを設定できますが、何かを echo
または print
すると
それはキャプチャされ、出力バッファリング経由でレスポンスボディとして送信されます。
Flight::route('/', function() {
Flight::response()->write("Hello, World!");
});
// 同じ意味です
Flight::route('/', function() {
echo "Hello, World!";
});
レスポンスボディのクリア
レスポンスボディをクリアしたい場合は、clearBody
メソッドを使用できます:
Flight::route('/', function() {
if($someCondition) {
Flight::response()->write("Hello, World!");
} else {
Flight::response()->clearBody();
}
});
レスポンスボディでコールバックを実行
addResponseBodyCallback
メソッドを使用してレスポンスボディでコールバックを実行できます:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
});
// これにより、すべてのルートのレスポンスが gzip 圧縮されます
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
複数のコールバックを追加でき、それらは追加された順に実行されます。このメソッドは任意の callable を受け入れるため、クラス配列 [ $class, 'method' ]
、クロージャ $strReplace = function($body) { str_replace('hi', 'there', $body); };
、または例えばHTMLコードを最適化する関数名 'minify'
を受け入れることができます。
注意: ルートコールバックは flight.v2.output_buffering
設定オプションを使用している場合には機能しません。
特定のルートコールバック
これを特定のルートにのみ適用したい場合は、ルート自体でコールバックを追加できます:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
// これにより、このルートのレスポンスのみが gzip 圧縮されます
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
});
ミドルウェアオプション
ミドルウェアを使用してすべてのルートにコールバックを適用することもできます:
// MinifyMiddleware.php
class MinifyMiddleware {
public function before() {
// レスポンス() オブジェクトにここでコールバックを適用します。
Flight::response()->addResponseBodyCallback(function($body) {
return $this->minify($body);
});
}
protected function minify(string $body): string {
// ボディを何らかの方法で最適化します
return $body;
}
}
// index.php
Flight::group('/users', function() {
Flight::route('', function() { /* ... */ });
Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);
レスポンスヘッダーの設定
header
メソッドを使用してレスポンスのコンテンツタイプなどのヘッダーを設定できます:
// これにより「Hello, World!」がプレーンテキストでユーザーのブラウザに送信されます
Flight::route('/', function() {
Flight::response()->header('Content-Type', 'text/plain');
// または
Flight::response()->setHeader('Content-Type', 'text/plain');
echo "Hello, World!";
});
JSON
Flight は JSON および JSONP レスポンスを送信するためのサポートを提供しています。JSON レスポンスを送信するには、いくつかのデータを JSON エンコードします:
Flight::json(['id' => 123]);
注意: デフォルトで、Flight はレスポンスに
Content-Type: application/json
ヘッダーを送信します。また、JSON をエンコードするときに定数JSON_THROW_ON_ERROR
およびJSON_UNESCAPED_SLASHES
を使用します。
ステータスコード付きの JSON
第二引数としてステータスコードを渡すこともできます:
Flight::json(['id' => 123], 201);
プレティプリント付きの JSON
最後の位置に引数を渡すことで、プレティプリントを有効にすることも可能です:
Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);
Flight::json()
に渡すオプションを変更して、よりシンプルな構文が必要な場合は、JSON メソッドを再マッピングすることができます:
Flight::map('json', function($data, $code = 200, $options = 0) {
Flight::_json($data, $code, true, 'utf-8', $options);
}
// これで、このように使うことができます
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);
JSON と実行の停止 (v3.10.0)
JSON レスポンスを送信し実行を停止する場合は、jsonHalt
メソッドを使用します。
これは、何らかの認証をチェックしていて、ユーザーが認証されていない場合に、すぐに JSON レスポンスを送信し、既存のボディコンテンツをクリアし、実行を停止できる便利な方法です。
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// ユーザーが認証されているかどうかをチェック
if($authorized === false) {
Flight::jsonHalt(['error' => 'Unauthorized'], 401);
}
// ルートの残りを続行
});
v3.10.0以前では、このようにする必要がありました:
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// ユーザーが認証されているかどうかをチェック
if($authorized === false) {
Flight::halt(401, json_encode(['error' => 'Unauthorized']));
}
// ルートの残りを続行
});
JSONP
JSONP リクエストについては、オプションでコールバック関数を定義するために使用しているクエリパラメータ名を渡すことができます:
Flight::jsonp(['id' => 123], 'q');
したがって、?q=my_func
を使用して GET リクエストを行うと、次の出力が得られます:
my_func({"id":123});
クエリパラメータ名を渡さない場合は、デフォルトで jsonp
に設定されます。
別の URL へのリダイレクト
redirect()
メソッドを使用して新しい URL を渡すことで、現在のリクエストをリダイレクトできます:
Flight::redirect('/new/location');
デフォルトでは Flight は HTTP 303 ("See Other") ステータスコードを送信します。オプションでカスタムコードを設定できます:
Flight::redirect('/new/location', 401);
停止
いつでも halt
メソッドを呼び出すことでフレームワークを停止できます:
Flight::halt();
オプションの HTTP
ステータスコードとメッセージを指定することもできます:
Flight::halt(200, 'Be right back...');
halt
を呼び出すと、その時点までのレスポンスコンテンツは破棄されます。フレームワークを停止し、現在のレスポンスを出力したい場合は、stop
メソッドを使用します:
Flight::stop();
レスポンスデータのクリア
clear()
メソッドを使用してレスポンスボディとヘッダーをクリアできます。これにより、レスポンスに割り当てられたヘッダーがクリアされ、レスポンスボディがクリアされ、ステータスコードが 200
に設定されます。
Flight::response()->clear();
レスポンスボディのみのクリア
レスポンスボディのみをクリアしたい場合は、clearBody()
メソッドを使用できます:
// これにより、レスポンス() オブジェクトに設定されたヘッダーは保持されます。
Flight::response()->clearBody();
HTTP キャッシング
Flight には、HTTP レベルのキャッシングに対するビルトインのサポートがあります。キャッシング条件が満たされると、Flight は HTTP 304 Not Modified
レスポンスを返します。クライアントが同じリソースを再度リクエストする際には、ローカルにキャッシュされたバージョンを使用するように促されます。
ルートレベルのキャッシング
レスポンス全体をキャッシュしたい場合は、cache()
メソッドを使用し、キャッシュする時間を渡すことができます。
// これにより、レスポンスが5分間キャッシュされます
Flight::route('/news', function () {
Flight::response()->cache(time() + 300);
echo 'このコンテンツはキャッシュされます。';
});
// または、strtotime() メソッドに渡す文字列を使用できます
Flight::route('/news', function () {
Flight::response()->cache('+5 minutes');
echo 'このコンテンツはキャッシュされます。';
});
最終更新日時
lastModified
メソッドを使用して、UNIX タイムスタンプを渡し、ページが最後に修正された日時を設定できます。クライアントは、最終更新値が変更されるまでキャッシュを使用し続けます。
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo 'このコンテンツはキャッシュされます。';
});
ETag
ETag
キャッシングは Last-Modified
と似ていますが、リソースのために望む任意の ID を指定できます:
Flight::route('/news', function () {
Flight::etag('my-unique-id');
echo 'このコンテンツはキャッシュされます。';
});
lastModified
または etag
を呼び出すと、どちらもキャッシュ値を設定およびチェックします。要求間でキャッシュ値が同じである場合、Flight は即座に HTTP 304
レスポンスを送信し、処理を停止します。
ファイルのダウンロード (v3.12.0)
ファイルをダウンロードするためのヘルパーメソッドがあります。download
メソッドを使用し、パスを渡すことができます。
Flight::route('/download', function () {
Flight::download('/path/to/file.txt');
});
Learn/frameworkinstance
フレームワークのインスタンス
Flightをグローバルな静的クラスとして実行する代わりに、オブジェクトのインスタンスとして実行することもできます。
require 'flight/autoload.php';
$app = Flight::app();
$app->route('/', function () {
echo 'hello world!';
});
$app->start();
静的なメソッドを呼び出す代わりに、同じ名前のインスタンスメソッドをEngineオブジェクトで呼び出すことになります。
Learn/redirects
リダイレクト
redirect
メソッドを使用して、新しい URL を指定して現在のリクエストをリダイレクトできます:
Flight::redirect('/new/location');
Flight はデフォルトで HTTP 303 ステータスコードを送信します。オプションでカスタムコードを設定できます:
Flight::redirect('/new/location', 401);
Learn/events
Flight PHP のイベントシステム (v3.15.0+)
Flight PHP は、軽量で直感的なイベントシステムを導入し、アプリケーションでカスタムイベントを登録およびトリガーできます。Flight::onEvent()
と Flight::triggerEvent()
の追加により、アプリのライフサイクルにおける重要なタイミングにフックしたり、自分でイベントを定義したりして、コードをよりモジュール化し拡張しやすくできます。これらのメソッドは Flight の マップ可能なメソッド であり、必要に応じて動作をオーバーライドできます。
このガイドでは、イベントの基礎知識から、なぜそれらが有用か、使い方、実践的な例までをカバーし、初心者がその力を理解する手助けをします。
なぜイベントを使うのか?
イベントを使うことで、アプリケーションの異なる部分を互いに過度に依存しないように分離できます。この分離(デカップリング と呼ばれる)は、コードの更新、拡張、デバッグを容易にします。一つの大きな塊で全てを書く代わりに、特定のアクション(イベント)に応答する小さな独立した部分にロジックを分割できます。
ブログアプリを作成していると想像してください:
- ユーザーがコメントを投稿したとき:
- コメントをデータベースに保存する。
- ブログオーナーにメールを送信する。
- セキュリティのためにアクションをログに記録する。
イベントを使わずにこれらを一つの関数に詰め込むことになりますが、イベントを使うと分割できます:一つの部分でコメントを保存し、もう一つの部分で 'comment.posted'
というイベントをトリガーし、別のリスナーがメール送信とログを処理します。これにより、コードがクリーンになり、機能(例: 通知)を追加または削除する際にコアロジックを触らずに済みます。
一般的な用途
- ログ記録: ログインやエラーのようなアクションを記録し、メイン�コードを散らかさない。
- 通知: 何かが起きたときにメールやアラートを送信する。
- 更新: キャッシュをリフレッシュしたり、他のシステムに変更を通知したりする。
イベントリスナーの登録
イベントをリッスンするには Flight::onEvent()
を使います。このメソッドでイベントが発生したときに何が起こるかを定義します。
構文
// イベント名とコールバックを指定します
Flight::onEvent(string $event, callable $callback): void
$event
: イベントの名前 (例:'user.login'
).$callback
: イベントがトリガーされたときに実行される関数。
動作の仕組み
イベントに「購読」することで、発生したときに何をするかを Flight に伝えます。コールバックはイベントトリガーから渡された引数を受け取ることができます。
Flight のイベントシステムは同期型です。つまり、各イベントリスナーは順番に実行され、すべての登録されたリスナーが完了するまでコードの実行が続きます。これは非同期のイベントシステムとは異なり、リスナーが並行して実行されたり後で実行されたりしない点が重要です。
簡単な例
// 'user.login' イベントがトリガーされたら、ユーザーを挨拶します
Flight::onEvent('user.login', function ($username) {
echo "Welcome back, $username!";
});
ここで、'user.login'
イベントがトリガーされると、ユーザーの名前で挨拶します。
重要なポイント
- 同じイベントに複数のリスナーを追加できます。それらは登録された順序で実行されます。
- コールバックは関数、匿名関数、またはクラスのメソッドにできます。
イベントのトリガー
イベントを発生させるには Flight::triggerEvent()
を使います。これにより、登録されたリスナーを実行し、必要なデータを渡します。
構文
// イベント名と任意の引数を指定します
Flight::triggerEvent(string $event, ...$args): void
$event
: トリガーするイベント名 (登録されたものと一致する必要があります)。...$args
: リスナーに渡すオプションの引数 (任意の数)。
簡単な例
$username = 'alice';
Flight::triggerEvent('user.login', $username);
これは 'user.login'
イベントをトリガーし、'alice'
を先に定義したリスナーに渡します。結果として、Welcome back, alice!
と出力されます。
重要なポイント
- リスナーが登録されていない場合、何も起こりません—アプリが壊れることはありません。
- スプレッドオペレーター (
...
) を使用して、柔軟に複数の引数を渡せます。
イベントリスナーの登録
...
さらなるリスナーの停止:
リスナーが false
を返す場合、そのイベントの追加のリスナーは実行されなくなります。これにより、特定の条件に基づいてイベントチェーンを停止できます。リスナーの順序が重要で、最初に false
を返すものが残りの実行を止めます。
例:
// ユーザーが禁止されている場合、ログアウトし、以降のリスナーを停止します
Flight::onEvent('user.login', function ($username) {
if (isBanned($username)) {
logoutUser($username);
return false; // 以降のリスナーを停止
}
});
Flight::onEvent('user.login', function ($username) {
sendWelcomeEmail($username); // これは実行されません
});
イベントメソッドのオーバーライド
Flight::onEvent()
と Flight::triggerEvent()
は 拡張 可能で、動作を再定義できます。これはイベントシステムをカスタマイズしたい上級ユーザーに便利です。例えば、ログの追加やイベントのディスパッチ方法の変更などです。
例: onEvent
のカスタマイズ
// イベント登録をログに記録します
Flight::map('onEvent', function (string $event, callable $callback) {
// 毎回のイベント登録をログに記録
error_log("New event listener added for: $event");
// 内部のデフォルト動作を呼び出す (内部イベントシステムを仮定)
Flight::_onEvent($event, $callback);
});
今度は、イベントを登録するたびにログが記録されます。
なぜオーバーライドするのか?
- デバッグや監視を追加する。
- 特定の環境でイベントを制限する (例: テスト環境で無効化)。
- 他のイベントライブラリと統合する。
イベントをどこに置くか
初心者の方は、アプリでこれらのイベントをどこに登録するのか? と疑問に思うかもしれません。Flight のシンプルさから厳格なルールはありませんが、整理しておくことでアプリが成長してもコードを維持しやすくなります。以下は実践的なオプションとベストプラクティスで、Flight の軽量性を考慮しています:
オプション 1: メインの index.php
ファイル内
小さなアプリやクイックプロトタイプの場合、イベントを index.php
ファイルにルートと一緒に登録できます。これにより全てが一つの場所にまとまり、シンプルさを優先できます。
require 'vendor/autoload.php';
// イベントを登録
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in at " . date('Y-m-d H:i:s'));
});
// ルートを定義
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- 利点: シンプルで、追加のファイル不要。小規模プロジェクトに最適。
- 欠点: アプリが成長すると、イベントとルートが増えて散らかりやすくなる。
オプション 2: 別々の events.php
ファイル
少し大きなアプリの場合、イベント登録を app/config/events.php
のような専用ファイルに移動し、index.php
でインクルードします。これは Flight プロジェクトでルートを整理するのと同じアプローチです。
// app/config/events.php
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in at " . date('Y-m-d H:i:s'));
});
Flight::onEvent('user.registered', function ($email, $name) {
echo "Email sent to $email: Welcome, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- 利点:
index.php
をルーティングに集中させ、イベントを論理的に整理。編集しやすく。 - 欠点: 非常に小さなアプリでは、構造を追加するのが過剰に感じるかも。
オプション 3: トリガーされる場所の近く
もう一つのアプローチは、イベントをトリガーされる場所に近い、例えばコントローラーやルート定義内に登録するものです。これがアプリの特定の部分に特化している場合に有効です。
Flight::route('/signup', function () {
// ここでイベントを登録
Flight::onEvent('user.registered', function ($email) {
echo "Welcome email sent to $email!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "Signed up!";
});
- 利点: 関連するコードを一緒に保ち、孤立した機能に適する。
- 欠点: イベント登録が散らばり、全イベントを一目で確認しにくくなる。重複のリスクあり。
Flight 向けのベストプラクティス
- シンプルから始める: 小さなアプリでは
index.php
にイベントを置く。Flight のミニマリズムに合っている。 - 賢く成長させる: アプリが拡大したら (例: 5-10 以上のイベント)、
app/config/events.php
ファイルを使う。ルートを整理するのと同じく、自然なステップアップ。 - 過度な設計を避ける: アプリが巨大にならない限り、フルブローの「イベントマネージャー」クラスやディレクトリを作成しない。Flight はシンプルさを活かすべき。
ヒント: 目的ごとにグループ化
events.php
では、関連するイベントをコメント付きでグループ化:
// app/config/events.php
// ユーザー関連のイベント
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in");
});
Flight::onEvent('user.registered', function ($email) {
echo "Welcome to $email!";
});
// ページ関連のイベント
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]);
});
この構造はスケーラブルで、初心者にも親しみやすい。
初心者向けの例
実際のシナリオを通じて、イベントがどのように動作し、なぜ役立つかを説明します。
例 1: ユーザーログインのログ記録
// ステップ 1: リスナーを登録
Flight::onEvent('user.login', function ($username) {
$time = date('Y-m-d H:i:s');
error_log("$username logged in at $time");
});
// ステップ 2: アプリ内でトリガー
Flight::route('/login', function () {
$username = 'bob'; // フォームから取得したと仮定
Flight::triggerEvent('user.login', $username);
echo "Hi, $username!";
});
なぜ役立つか: ログインコードはログについて知らなくてもイベントをトリガーするだけです。後でリスナーを追加 (例: ウェルカムメール) してもルートを変更せずに済みます。
例 2: 新規ユーザーの通知
// 新規登録のリスナー
Flight::onEvent('user.registered', function ($email, $name) {
// メール送信をシミュレート
echo "Email sent to $email: Welcome, $name!";
});
// サインアップ時にトリガー
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "Thanks for signing up!";
});
なぜ役立つか: サインアップロジックはユーザー作成に集中し、イベントが通知を処理します。後でリスナーを追加 (例: サインアップのログ) できます。
例 3: キャッシュのクリア
// キャッシュをクリアするリスナー
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]); // セッションキャッシュをクリア
echo "Cache cleared for page $pageId.";
});
// ページを編集したときにトリガー
Flight::route('/edit-page/(@id)', function ($pageId) {
// ページを更新したと仮定
Flight::triggerEvent('page.updated', $pageId);
echo "Page $pageId updated.";
});
なぜ役立つか: 編集コードはキャッシングを気にせず、更新をシグナルするだけです。他の部分が対応できます。
ベストプラクティス
- イベント名を明確に:
'user.login'
や'page.updated'
のように具体的な名前を使い、何をするのかを明示する。 - リスナーをシンプルに: 遅いタスクや複雑な処理をリスナーに入れない—アプリを高速に保つ。
- イベントをテスト: 手動でトリガーして、期待通りに動作することを確認。
- イベントを賢く使う: デカップリングに最適だが、多用しすぎるとコードが追いにくくなる—必要に応じて。
Flight PHP のイベントシステムは、Flight::onEvent()
と Flight::triggerEvent()
により、シンプルでありながら強力な柔軟なアプリケーション構築を可能にします。イベントを通じてアプリの異なる部分が通信することで、コードを整理し、再利用しやすく、拡張しやすくします。アクションのログ、通知の送信、更新の管理など、イベントがあればロジックを絡ませずに実現できます。さらに、これらのメソッドをオーバーライドできるので、システムをニーズに合わせてカスタマイズできます。単一のイベントから始め、アプリの構造がどのように変わるかを見てください!
ビルトインイベント
Flight PHP には、フレームワークのライフサイクルにフックするためのビルトインイベントがいくつか用意されています。これらのイベントは、リクエスト/レスポンスサイクルの特定のタイミングでトリガーされ、カスタムロジックを実行できます。
ビルトインイベント一覧
- flight.request.received:
function(Request $request)
リクエストが受信され、解析・処理されたときにトリガー。 - flight.error:
function(Throwable $exception)
リクエストライフサイクル中にエラーが発生したときにトリガー。 - flight.redirect:
function(string $url, int $status_code)
リダイレクトが開始されたときにトリガー。 - flight.cache.checked:
function(string $cache_key, bool $hit, float $executionTime)
特定のキーのキャッシュがチェックされ、ヒットしたかどうかのときにトリガー。 - flight.middleware.before:
function(Route $route)
ビフォアミドルウェアが実行された後にトリガー。 - flight.middleware.after:
function(Route $route)
アフターミドルウェアが実行された後にトリガー。 - flight.middleware.executed:
function(Route $route, $middleware, string $method, float $executionTime)
任意のミドルウェアが実行された後にトリガー。 - flight.route.matched:
function(Route $route)
ルートがマッチしたが、まだ実行されていないときにトリガー。 - flight.route.executed:
function(Route $route, float $executionTime)
ルートが実行され、処理された後にトリガー。$executionTime
はルート実行にかかった時間。 - flight.view.rendered:
function(string $template_file_path, float $executionTime)
ビューがレンダリングされた後にトリガー。$executionTime
はテンプレートのレンダリングにかかった時間。注意:render
メソッドをオーバーライドした場合は、このイベントを再トリガーする必要があります。 - flight.response.sent:
function(Response $response, float $executionTime)
レスポンスがクライアントに送信された後にトリガー。$executionTime
はレスポンスの構築にかかった時間。
Learn/views
ビュー
Flightは、デフォルトでいくつかの基本的なテンプレーティング機能を提供します。ビューテンプレートを表示するには、render
メソッドをテンプレートファイルの名前とオプションのテンプレートデータで呼び出します:
Flight::render('hello.php', ['name' => 'Bob']);
渡すテンプレートデータは、自動的にテンプレートに注入され、ローカル変数のように参照できます。テンプレートファイルは単純なPHPファイルです。hello.php
テンプレートファイルの内容が次のような場合:
Hello, <?= $name ?>!
出力は次のようになります:
Hello, Bob!
また、set
メソッドを使用してビュー変数を手動で設定することもできます:
Flight::view()->set('name', 'Bob');
変数name
は今やすべてのビューで利用可能です。したがって、次のように簡単にできます:
Flight::render('hello');
render
メソッド内でテンプレートの名前を指定する際に、.php
拡張子を省略することができることに注意してください。
デフォルトでは、Flightはテンプレートファイルのために views
ディレクトリを参照します。テンプレートの代替パスを設定するためには、次の設定を行います:
Flight::set('flight.views.path', '/path/to/views');
レイアウト
ウェブサイトには、入れ替わるコンテンツを持つ単一のレイアウトテンプレートファイルを持つことが一般的です。レイアウトにレンダリングするコンテンツを渡すには、render
メソッドにオプションのパラメータを渡すことができます。
Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');
その後、ビューには headerContent
と bodyContent
という名前の保存された変数があります。次に、次のようにしてレイアウトをレンダリングできます:
Flight::render('layout', ['title' => 'Home Page']);
テンプレートファイルが次のようになっている場合:
header.php
:
<h1><?= $heading ?></h1>
body.php
:
<div><?= $body ?></div>
layout.php
:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $headerContent ?>
<?= $bodyContent ?>
</body>
</html>
出力は次のようになります:
<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Hello</h1>
<div>World</div>
</body>
</html>
カスタムビュー
Flightを使用すると、独自のビュークラスを登録するだけでデフォルトのビューエンジンを切り替えることができます。ビューにSmartyテンプレートエンジンを使用する方法は次の通りです:
// Smartyライブラリの読み込み
require './Smarty/libs/Smarty.class.php';
// ビュークラスとしてSmartyを登録
// Smartyをロード時に構成するためのコールバック関数も渡す
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
$smarty->setTemplateDir('./templates/');
$smarty->setCompileDir('./templates_c/');
$smarty->setConfigDir('./config/');
$smarty->setCacheDir('./cache/');
});
// テンプレートデータを割り当てる
Flight::view()->assign('name', 'Bob');
// テンプレートを表示
Flight::view()->display('hello.tpl');
完全性を期すために、Flightのデフォルトのrender
メソッドもオーバーライドする必要があります:
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Learn/templates
HTML ビューとテンプレート
Flight はデフォルトで基本的なテンプレーティング機能を提供します。
Flight を使用すると、独自のビュークラスを登録するだけでデフォルトのビューエンジンを切り替えることができます。Smarty、Latte、Blade などの使用例を以下で確認してください!
組み込みビューエンジン
ビュー テンプレートを表示するには、テンプレートファイルの名前とオプションのテンプレートデータを使って render
メソッドを呼び出します:
Flight::render('hello.php', ['name' => 'Bob']);
渡されたテンプレートデータは自動的にテンプレートに注入され、ローカル変数のように参照できます。テンプレートファイルは単純な PHP ファイルです。hello.php
テンプレートファイルの内容が次のようである場合:
Hello, <?= $name ?>!
出力は次のようになります:
Hello, Bob!
また、set メソッドを使用してビュー変数を手動で設定することもできます:
Flight::view()->set('name', 'Bob');
変数 name
はすべてのビューで利用可能になりました。ですので、単純に次のようにできます:
Flight::render('hello');
render メソッドでテンプレートの名前を指定する際には、.php
拡張子を省略することもできます。
デフォルトでは、Flight はテンプレートファイル用に views
ディレクトリを探します。次の設定を行うことで、テンプレート用の別のパスを設定できます:
Flight::set('flight.views.path', '/path/to/views');
レイアウト
ウェブサイトには、入れ替え可能なコンテンツを持つ単一のレイアウトテンプレートファイルが一般的です。レイアウトで使用するコンテンツをレンダリングするには、render
メソッドにオプションのパラメータを渡すことができます。
Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');
これにより、headerContent
と bodyContent
という名前の保存された変数を持つことができます。そして、次のようにしてレイアウトをレンダリングできます:
Flight::render('layout', ['title' => 'Home Page']);
テンプレートファイルが次のようである場合:
header.php
:
<h1><?= $heading ?></h1>
body.php
:
<div><?= $body ?></div>
layout.php
:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $headerContent ?>
<?= $bodyContent ?>
</body>
</html>
出力は次のようになります:
<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Hello</h1>
<div>World</div>
</body>
</html>
Smarty
ビュー用の Smarty テンプレートエンジンを使用する方法は以下の通りです:
// Smarty ライブラリを読み込みます
require './Smarty/libs/Smarty.class.php';
// Smarty をビュークラスとして登録します
// Smarty をロード時に設定するためのコールバック関数も渡します
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
$smarty->setTemplateDir('./templates/');
$smarty->setCompileDir('./templates_c/');
$smarty->setConfigDir('./config/');
$smarty->setCacheDir('./cache/');
});
// テンプレートデータを割り当てます
Flight::view()->assign('name', 'Bob');
// テンプレートを表示します
Flight::view()->display('hello.tpl');
完全性のために、Flight のデフォルトの render メソッドをオーバーライドする必要があります:
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Latte
ビュー用の Latte テンプレートエンジンを使用する方法は以下の通りです:
// Latte をビュークラスとして登録します
// Latte をロード時に設定するためのコールバック関数も渡します
Flight::register('view', Latte\Engine::class, [], function (Latte\Engine $latte) {
// ここが Latte がテンプレートをキャッシュして速度を向上させる場所です
// Latte の一つの素晴らしい点は、テンプレートに変更を加えると自動的にキャッシュを更新することです!
$latte->setTempDirectory(__DIR__ . '/../cache/');
// ビューのルートディレクトリがどこになるかを Latte に教えます
$latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../views/'));
});
// Flight::render() を正しく使用できるようにラップします
Flight::map('render', function(string $template, array $data): void {
// これは $latte_engine->render($template, $data)のようなものです
echo Flight::view()->render($template, $data);
});
Blade
ビュー用の Blade テンプレートエンジンを使用する方法は以下の通りです:
まず、Composer を使用して BladeOne ライブラリをインストールする必要があります:
composer require eftec/bladeone
次に、Flight で BladeOne をビュークラスとして設定できます:
<?php
// BladeOne ライブラリを読み込みます
use eftec\bladeone\BladeOne;
// BladeOne をビュークラスとして登録します
// BladeOne をロード時に設定するためのコールバック関数も渡します
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
$views = __DIR__ . '/../views';
$cache = __DIR__ . '/../cache';
$blade->setPath($views);
$blade->setCompiledPath($cache);
});
// テンプレートデータを割り当てます
Flight::view()->share('name', 'Bob');
// テンプレートを表示します
echo Flight::view()->run('hello', []);
完全性のために、Flight のデフォルトの render メソッドもオーバーライドする必要があります:
<?php
Flight::map('render', function(string $template, array $data): void {
echo Flight::view()->run($template, $data);
});
この例では、hello.blade.php テンプレートファイルは次のようになります:
<?php
Hello, {{ $name }}!
出力は次のようになります:
Hello, Bob!
これらの手順に従うことで、Blade テンプレートエンジンを Flight に統合し、ビューをレンダリングすることができます。
Learn/flight_vs_fat_free
Fat-Free vs Flight
何がFat-Freeか?
Fat-Free(愛称F3)は、迅速に動的かつ堅牢なウェブアプリケーションを構築するのに役立つ強力で使いやすいPHPマイクロフレームワークです。
Flightは多くの点でFat-Freeと比較され、機能とシンプリシティの面ではおそらく最も近しい親戚です。 Fat-FreeにはFlightにはない機能が多く含まれていますが、Flightにはある機能も多くあります。 Fat-Freeは時代遅れになりつつあり、かつてほど人気がありません。
更新頻度が低くなり、コミュニティも以前ほど活発ではありません。コードは十分にシンプルですが、構文の規律が欠如していることが時々読み取りやすさを損なうことがあります。PHP 8.3でも動作しますが、コード自体はまだPHP 5.3であるかのように見えます。
Flightと比較したPros
- Fat-FreeにはFlightよりもGitHubでいくつかのスターが多い。
- Fat-Freeにはいくつかのきちんとしたドキュメントがありますが、明確さに欠ける部分もあります。
- Fat-Freeには、フレームワークを学ぶのに使用できるYouTubeチュートリアルやオンライン記事など、いくつかのスカスカリソースがあります。
- Fat-Freeには時々役立ついくつかのプラグインが組み込まれています。
- Fat-Freeには、データベースとやり取りするために使用できるMapperと呼ばれる組み込みのORMがあります。Flightにはactive-recordがあります。
- Fat-Freeにはセッション、キャッシング、ローカライゼーションが組み込まれています。Flightではサードパーティライブラリを使用する必要がありますが、ドキュメントでカバーされています。
- Fat-Freeには、フレームワークを拡張するために使用できるコミュニティ作成のプラグインが少数あります。Flightにはドキュメントと例ページでカバーされています。
- Fat-FreeはFlight同様に依存関係がありません。
- Fat-FreeはFlight同様に開発者がアプリケーションを制御し、シンプルな開発体験を提供することを目的としています。
- Fat-Freeは更新が少なくなってきているため、Flightと同様に後方互換性を維持しています。
- Fat-FreeはFlight同様に、フレームワークの世界に初めて足を踏み入れる開発者を対象としています。
- Fat-Freeには、Flightのテンプレートエンジンよりも堅牢な組み込みのテンプレートエンジンがあります。Flightはこれを達成するためにLatteを推奨しています。
- Fat-Freeには、「route」と呼ばれるユニークなCLI型コマンドがあり、Fat-Free自体内でCLIアプリを構築して、それをGETリクエストのように処理できます。Flightはこれをrunwayで実現しています。
Flightと比較したCons
- Fat-Freeには一部の実装テストがあり、非常に基本的な自社のtest クラスがありますが、Flightのように100%ユニットテストされていません。
- ドキュメントサイトを実際に検索するにはGoogleのような検索エンジンを使用する必要があります。
- Flightのドキュメントサイトにはダークモードがあります。(マイクを落とす)
- Fat-Freeにはメンテナンスされていないモジュールがいくつかあります。
- Flightには、Fat-Freeの組み込みの
DB \ SQL
クラスよりも少しシンプルなPdoWrapperがあります。 - Flightにはアプリケーションを保護するために使用できるpermissionsプラグインがあります。Slimではサードパーティライブラリを使用する必要があります。
- Flightには、Fat-FreeのMapperよりもORMらしいactive-recordがあります。
active-record
の追加メリットは、Fat-FreeのMapperがSQLビューを作成する必要があるのに対し、レコード間の関係を定義して自動結合することができます。 - 驚くべきことに、Fat-Freeにはルート名前空間がありません。Flightは、独自のコードと衝突しないようにすべての方法で名前空間が付けられています。
Cache
クラスが最も問題があります。 - Fat-Freeにはミドルウェアがありません。代わりに、リクエストとレスポンスをフィルタリングするために使用できる
beforeroute
およびafterroute
フックがあります。 - Fat-Freeでは、ルートをグループ化することはできません。
- Fat-Freeには依存性注入コンテナハンドラがありますが、その使用方法に関するドキュメントが非常にわずかです。
- デバッギングは、基本的にすべてが
HIVE
に保存されているため、少し複雑になることがあります。
Learn/extending
拡張
Flightは拡張可能なフレームワークとして設計されています。このフレームワークにはデフォルトのメソッドとコンポーネントのセットが付属していますが、独自のメソッドをマッピングしたり、自分のクラスを登録したり、既存のクラスやメソッドをオーバーライドすることもできます。
DIC(依存性注入コンテナ)を探しているなら、依存性注入コンテナ ページをご覧ください。
メソッドのマッピング
独自のシンプルなカスタムメソッドをマップするには、map
関数を使用します:
// あなたのメソッドをマップする
Flight::map('hello', function (string $name) {
echo "hello $name!";
});
// あなたのカスタムメソッドを呼び出す
Flight::hello('Bob');
シンプルなカスタムメソッドを作成することは可能ですが、PHPで標準関数を作成することをお勧めします。これはIDEでオートコンプリートがあり、読みやすくなります。 上記のコードの同等のものは次のようになります:
function hello(string $name) {
echo "hello $name!";
}
hello('Bob');
これは、期待される値を得るためにメソッドに変数を渡す必要があるときにもっと使われます。以下のようにregister()
メソッドを使用するのは、設定を渡し、あらかじめ設定されたクラスを呼び出すためのものです。
クラスの登録
独自のクラスを登録して設定するには、register
関数を使用します:
// あなたのクラスを登録する
Flight::register('user', User::class);
// あなたのクラスのインスタンスを取得する
$user = Flight::user();
registerメソッドは、クラスのコンストラクタにパラメータを渡すことも可能です。したがって、カスタムクラスをロードするとき、それは事前に初期化されていることになります。 コンストラクタのパラメータは、追加の配列を渡すことで定義できます。 データベース接続をロードする例は次のとおりです:
// コンストラクタパラメータ付きでクラスを登録する
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'user', 'pass']);
// あなたのクラスのインスタンスを取得する
// これは定義されたパラメータを持つオブジェクトを作成します
//
// new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();
// そして、もしコード内で後でそれが必要になった場合は、再度同じメソッドを呼び出すだけです
class SomeController {
public function __construct() {
$this->db = Flight::db();
}
}
追加のコールバックパラメータを渡すと、クラスの構築後に直ちに実行されます。これにより、新しいオブジェクトのために設定手順を実行できます。コールバック関数は1つのパラメータ、新しいオブジェクトのインスタンスを受け取ります。
// コールバックには構築されたオブジェクトが渡されます
Flight::register(
'db',
PDO::class,
['mysql:host=localhost;dbname=test', 'user', 'pass'],
function (PDO $db) {
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
);
デフォルトでは、クラスを読み込むたびに共有インスタンスが得られます。
クラスの新しいインスタンスを取得するには、false
をパラメータとして渡すだけです:
// クラスの共有インスタンス
$shared = Flight::db();
// クラスの新しいインスタンス
$new = Flight::db(false);
マッピングされたメソッドは、登録されたクラスよりも優先されることに注意してください。両方を同じ名前で宣言した場合、マッピングされたメソッドのみが呼び出されます。
ロギング
Flightには組み込みのロギングシステムはありませんが、Flightとともにロギングライブラリを使用するのは非常に簡単です。以下はMonologライブラリを使用した例です:
// index.phpまたはbootstrap.php
// Flightにロガーを登録する
Flight::register('log', Monolog\Logger::class, [ 'name' ], function(Monolog\Logger $log) {
$log->pushHandler(new Monolog\Handler\StreamHandler('path/to/your.log', Monolog\Logger::WARNING));
});
登録されたので、アプリケーションで使用することができます:
// あなたのコントローラやルートの中で
Flight::log()->warning('これは警告メッセージです');
これは、指定されたログファイルにメッセージを記録します。エラーが発生したときに何かをログに記録したい場合は、error
メソッドを使用できます:
// あなたのコントローラやルートの中で
Flight::map('error', function(Throwable $ex) {
Flight::log()->error($ex->getMessage());
// あなたのカスタムエラーページを表示する
include 'errors/500.html';
});
また、before
とafter
メソッドを使用して基本的なAPM(アプリケーションパフォーマンスモニタリング)システムを作成することもできます:
// あなたのブートストラップファイルの中で
Flight::before('start', function() {
Flight::set('start_time', microtime(true));
});
Flight::after('start', function() {
$end = microtime(true);
$start = Flight::get('start_time');
Flight::log()->info('リクエスト '.Flight::request()->url.' は ' . round($end - $start, 4) . ' 秒かかりました');
// あなたのリクエストまたはレスポンスヘッダーを追加することもできます
// それらをログに記録するために(多くのリクエストがあるときはデータが大量になるので注意してください)
Flight::log()->info('リクエストヘッダー: ' . json_encode(Flight::request()->headers));
Flight::log()->info('レスポンスヘッダー: ' . json_encode(Flight::response()->headers));
});
フレームワークメソッドのオーバーライド
Flightは、コードを修正することなく、デフォルトの機能を自分のニーズに合わせてオーバーライドすることを可能にします。オーバーライドできるすべてのメソッドをこちらで確認できます。
たとえば、FlightがURLをルートに一致させることができない場合、notFound
メソッドが呼び出され、一般的なHTTP 404
レスポンスが送信されます。この動作をオーバーライドするには、map
メソッドを使用します:
Flight::map('notFound', function() {
// カスタム404ページを表示する
include 'errors/404.html';
});
Flightはフレームワークのコアコンポーネントを置き換えることもできます。 たとえば、デフォルトのRouterクラスを独自のカスタムクラスに置き換えることができます:
// あなたのカスタムクラスを登録する
Flight::register('router', MyRouter::class);
// FlightがRouterインスタンスをロードするとき、あなたのクラスがロードされます
$myrouter = Flight::router();
ただし、map
やregister
のようなフレームワークメソッドはオーバーライドできません。そうしようとするとエラーが発生します。
Learn/json
JSON
FlightはJSONとJSONPレスポンスを送信するためのサポートを提供します。 JSONレスポンスを送信するには、JSONエンコードするデータを渡します:
Flight::json(['id' => 123]);
JSONPリクエストの場合は、コールバック関数を定義するために使用するクエリパラメータ名をオプションで指定できます:
Flight::jsonp(['id' => 123], 'q');
そのため、?q=my_func
を使用してGETリクエストを行うと、次の出力が返されるはずです:
my_func({"id":123});
クエリパラメータ名を指定しない場合、デフォルトでjsonp
になります。
Learn/flight_vs_slim
Flight vs Slim
Slimとは?
Slim は、シンプルでありながらパワフルなウェブアプリケーションやAPIを素早く作成できるPHPマイクロフレームワークです。
v3の一部機能に対するインスピレーションの多くは、実際にはSlimからきています。ルートのグループ化や、ミドルウェアの特定の順序での実行といった2つの機能はSlimからの影響を受けています。Slim v3はシンプルさを重視した形でリリースされましたが、v4に関しては賛否両論があります。
Flightと比較したメリット
- Slimには、車輪の再発明を避けるのに役立つ便利なモジュールを作る開発者コミュニティが大規模です。
- Slimは、PHPコミュニティで一般的な多くのインターフェースと規格に従っており、相互運用性が高まっています。
- Slimには、フレームワークの学習に使用できる、まずまずのドキュメントやチュートリアルがあります(ただし、LaravelやSymfonyとは比べものになりません)。
- Slimには、フレームワークの学習に使用できるYouTubeチュートリアルやオンライン記事など、さまざまなリソースがあります。
- Slimを使用すると、PSR-7に準拠しているため、コアルーティング機能を処理するために必要なコンポーネントを自由に選択できます。
Flightと比較したデメリット
- 意外なことに、Slimはマイクロフレームワークとして考えるよりも速くありません。詳細については、TechEmpower benchmarksを参照してください。
- Flightは、軽量で高速かつ使いやすいウェブアプリケーションを構築したい開発者を対象としています。
- Flightには依存関係がなく、一方、Slimにはいくつかの依存関係があり、インストールする必要があります。
- Flightはシンプルさと使いやすさを重視しています。
- Flightの中核的な機能の1つは、後方互換性を維持することです。Slim v3からv4への移行は互換性がない変更でした。
- Flightは、初めてフレームワークの世界に足を踏み入れる開発者を対象としています。
- Flightはエンタープライズレベルのアプリケーションも可能ですが、Slimほどの例やチュートリアルがないため、開発者が組織化し、構造化されたものを保つためにより多くの努力が必要です。
- Flightは開発者にアプリケーションの制御権を与え、一方、Slimは一部のマジックを裏で使用することがあります。
- Flightには、データベースとやり取りするために使用できるシンプルなPdoWrapperがあります。Slimではサードパーティのライブラリを使用する必要があります。
- Flightにはアプリケーションをセキュリティで保護するために使用できるpermissionsプラグインがあります。Slimでは、サードパーティのライブラリを使用する必要があります。
- Flightには、データベースとやり取りするために使用できるactive-recordと呼ばれるORMがあります。Slimでは、サードパーティのライブラリを使用する必要があります。
- Flightには、アプリケーションをコマンドラインから実行するために使用できるrunwayと呼ばれるCLIアプリケーションがあります。Slimにはありません。
Learn/autoloading
オートローディング
オートローディングは、PHPにおいてクラスを読み込むディレクトリを指定する概念です。これは、require
やinclude
を使用してクラスをロードするよりも有益です。Composerパッケージを使用する際にも必要です。
デフォルトでは、Flight
クラスはComposerのおかげで自動的にオートロードされます。ただし、独自のクラスをオートロードする場合は、Flight::path()
メソッドを使用してクラスを読み込むディレクトリを指定できます。
基本例
以下のようなディレクトリツリーを持つとします:
# 例えばのパス
/home/user/project/my-flight-project/
├── app
│ ├── cache
│ ├── config
│ ├── controllers - このプロジェクトのコントローラーが含まれる
│ ├── translations
│ ├── UTILS - このアプリケーション専用のクラスが含まれる(これは後の例のためにわざと全てキャピタライズされています)
│ └── views
└── public
└── css
└── js
└── index.php
このドキュメンテーションサイトと同じファイル構造であることに気づかれたかもしれません。
次のように各ディレクトリを指定できます:
/**
* public/index.php
*/
// オートローダーにパスを追加
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
/**
* app/controllers/MyController.php
*/
// 名前空間は必要ありません
// すべてのオートロードされるクラスはパスカルケース(各単語を大文字にして、スペースなし)であることが推奨されます
// ローダー::setV2ClassLoading(false);を実行することで、クラス名にパスカル_スネーク_ケースを使用できます(バージョン3.7.2以降)
class MyController {
public function index() {
// 何かを実行
}
}
名前空間
名前空間がある場合、これを実装するのは実際には非常に簡単です。Flight::path()
メソッドを使用して、アプリケーションのルートディレクトリ(ドキュメントルートや public/
フォルダではない)を指定する必要があります。
/**
* public/index.php
*/
// オートローダーにパスを追加
Flight::path(__DIR__.'/../');
これがあなたのコントローラーの見た目です。以下の例を見てくださいが、重要な情報はコメントに注目してください。
/**
* app/controllers/MyController.php
*/
// 名前空間は必須です
// 名前空間はディレクトリ構造と同じです
// 名前空間はディレクトリ構造と同じケースを使用する必要があります
// 名前空間とディレクトリにはアンダースコアを含めることはできません(Loader::setV2ClassLoading(false)が設定されていない限り)
namespace app\controllers;
// すべてのオートロードされるクラスはパスカルケース(各単語を大文字にして、スペースなし)であることが推奨されます
// ローダー::setV2ClassLoading(false);を実行することで、クラス名にパスカル_スネーク_ケースを使用できます(バージョン3.7.2以降)
class MyController {
public function index() {
// 何かを実行
}
}
それと、utilsディレクトリ内のクラスをオートロードしたい場合は、基本的に同じことを行います:
/**
* app/UTILS/ArrayHelperUtil.php
*/
// 名前空間はディレクトリ構造とケースと一致する必要があります(UTILSディレクトリがファイルツリー内で全てキャピタライズされていることに注意)
namespace app\UTILS;
class ArrayHelperUtil {
public function changeArrayCase(array $array) {
// 何かを実行
}
}
クラス名にアンダースコアが含まれる場合
バージョン3.7.2以降、Loader::setV2ClassLoading(false);
を実行することで、クラス名にパスカル_スネーク_ケースを使用できます。これにより、クラス名にアンダースコアを使用できます。これは推奨されませんが、必要な方には利用可能です。
/**
* public/index.php
*/
// オートローダーにパスを追加
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
Loader::setV2ClassLoading(false);
/**
* app/controllers/My_Controller.php
*/
// 名前空間は必要ありません
class My_Controller {
public function index() {
// 何かを実行
}
}
Learn/troubleshooting
トラブルシューティング
このページでは、Flightを使用している際に遭遇するかもしれない一般的な問題のトラブルシューティングを支援します。
一般的な問題
404 Not Found または予期しないルートの動作
404 Not Found エラーが表示される場合(しかし、それが実際に存在していることを誓って、タイプミスではないと主張する場合)、実際にはこれは、単にそれをエコーするのではなく、ルートエンドポイントで値を返すことが問題である可能性があります。これは意図的に行われている理由ですが、開発者の一部には忍び込む可能性があります。
Flight::route('/hello', function(){
// これが 404 Not Found エラーの原因となる可能性があります
return 'Hello World';
});
// おそらく望む動作
Flight::route('/hello', function(){
echo 'Hello World';
});
これは、ルーターに組み込まれている特別なメカニズムのために行われます。このメカニズムは、戻り出力を単一の「次のルートに移動する」として処理します。この動作はRoutingセクションで文書化されています。
クラスが見つかりません(オートローディングが機能していない)
これにはいくつかの理由が考えられます。以下にいくつかの例を示しますが、autoloadingセクションも確認してください。
ファイル名が間違っています
最も一般的なのは、クラス名がファイル名と一致していないことです。
クラス名が MyClass
の場合、ファイル名は MyClass.php
とする必要があります。クラス名が MyClass
でファイル名が myclass.php
の場合、オートローダーはそれを見つけることができません。
名前空間が正しくありません
名前空間を使用している場合、名前空間はディレクトリ構造と一致している必要があります。
// コード
// もし MyController が app/controllers ディレクトリにあり、名前空間が付いている場合
// この方法は機能しません。
Flight::route('/hello', 'MyController->hello');
// 以下のオプションのいずれかを選択する必要があります
Flight::route('/hello', 'app\controllers\MyController->hello');
// または先頭に use 文がある場合
use app\controllers\MyController;
Flight::route('/hello', [ MyController::class, 'hello' ]);
// また、以下のように記述することもできます
Flight::route('/hello', MyController::class.'->hello');
// また...
Flight::route('/hello', [ 'app\controllers\MyController', 'hello' ]);
path()
が定義されていません
スケルトンアプリでは、これは config.php
ファイル内で定義されていますが、クラスを見つけるためには、使用する前に path()
メソッドが定義されていることを確認する必要があります(おそらくディレクトリのルートに)。
// オートローダーにパスを追加
Flight::path(__DIR__.'/../');
Install
インストール
ファイルをダウンロードします。
もしComposerを使用している場合、次のコマンドを実行できます:
composer require flightphp/core
または、ファイルをダウンロードして、それらをウェブディレクトリに直接展開することもできます。
ウェブサーバを構成します。
Apache
Apacheを使用する場合、.htaccess
ファイルを以下のように編集してください:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
注意: サブディレクトリでflightを使用する必要がある場合は、
RewriteEngine On
の直後に行を追加してください:RewriteBase /subdir/
。注意: データベースや環境ファイルなどのすべてのサーバファイルを保護する必要がある場合は、
.htaccess
ファイルに以下を追加してください:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Nginxを使用する場合、以下をサーバ定義に追加してください:
server {
location / {
try_files $uri $uri/ /index.php;
}
}
index.php
ファイルを作成します。
<?php
// Composerを使用している場合、オートローダーを要求します。
require 'vendor/autoload.php';
// Composerを使用していない場合、フレームワークを直接ロードします
// require 'flight/Flight.php';
// 次に、ルートを定義し、リクエストを処理するための関数を割り当てます。
Flight::route('/', function () {
echo 'hello world!';
});
// 最後に、フレームワークをスタートします。
Flight::start();
Guides/blog
Flight PHPを使ったシンプルなブログの構築
このガイドでは、Flight PHPフレームワークを使用して基本的なブログを作成する方法を説明します。プロジェクトをセットアップし、ルートを定義し、JSONを使用して投稿を管理し、Latteテンプレーティングエンジンでレンダリングします。すべてがFlightのシンプルさと柔軟性を示しています。最後には、ホームページ、個別の投稿ページ、および作成フォームを持つ機能的なブログが完成します。
前提条件
- PHP 7.4+: システムにインストールされていること。
- Composer: 依存関係管理用。
- テキストエディタ: VS CodeやPHPStormなどの任意のエディタ。
- PHPとWeb開発の基本知識。
ステップ1: プロジェクトのセットアップ
新しいプロジェクトディレクトリを作成し、Composerを介してFlightをインストールします。
-
ディレクトリの作成:
mkdir flight-blog cd flight-blog
-
Flightのインストール:
composer require flightphp/core
-
パブリックディレクトリの作成: Flightは単一のエントリーポイント(
index.php
)を使用します。それ用にpublic/
フォルダを作成します:mkdir public
-
基本的な
index.php
: シンプルな「Hello World」ルートを持つpublic/index.php
を作成します:<?php require '../vendor/autoload.php'; Flight::route('/', function () { echo 'こんにちは、Flight!'; }); Flight::start();
-
組み込みサーバーの起動: PHPの開発サーバーを使用してセットアップをテストします:
php -S localhost:8000 -t public/
http://localhost:8000
にアクセスして「こんにちは、Flight!」を見ることができます。
ステップ2: プロジェクト構造の整理
クリーンなセットアップのために、プロジェクトを以下のように構成します:
flight-blog/
├── app/
│ ├── config/
│ └── views/
├── data/
├── public/
│ └── index.php
├── vendor/
└── composer.json
app/config/
: 設定ファイル(例:イベント、ルート)。app/views/
: ページをレンダリングするためのテンプレート。data/
: ブログ投稿を保存するためのJSONファイル。public/
:index.php
を含むWebルート。
ステップ3: Latteのインストールと設定
Latteは、Flightとよく統合される軽量なテンプレーティングエンジンです。
-
Latteのインストール:
composer require latte/latte
-
FlightでのLatteの設定:
public/index.php
を更新してLatteをビューエンジンとして登録します:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => '私のブログ']); }); Flight::start();
-
レイアウトテンプレートを作成する:
app/views/layout.latte
で:<!DOCTYPE html> <html> <head> <title>{$title}</title> </head> <body> <header> <h1>私のブログ</h1> <nav> <a href="/">ホーム</a> | <a href="/create">投稿を作成</a> </nav> </header> <main> {block content}{/block} </main> <footer> <p>© {date('Y')} Flightブログ</p> </footer> </body> </html>
-
ホームテンプレートを作成:
app/views/home.latte
で:{extends 'layout.latte'} {block content} <h2>{$title}</h2> <ul> {foreach $posts as $post} <li><a href="/post/{$post['slug']}">{$post['title']}</a></li> {/foreach} </ul> {/block}
サーバーを再起動して、
http://localhost:8000
にアクセスしてレンダリングされたページを確認してください。 -
データファイルを作成:
簡単のためにデータベースのシミュレーションとしてJSONファイルを使用します。
data/posts.json
で:[ { "slug": "first-post", "title": "私の最初の投稿", "content": "これはFlight PHPを使用した私の初めてのブログ投稿です!" } ]
ステップ4: ルートの定義
ルートを構成ファイルに分けることで、整理を良くしましょう。
-
routes.php
を作成:app/config/routes.php
で:<?php Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => '私のブログ']); }); Flight::route('/post/@slug', function ($slug) { Flight::view()->render('post.latte', ['title' => '投稿: ' . $slug, 'slug' => $slug]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => '投稿を作成']); });
-
index.php
を更新: ルートファイルを含める:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); require '../app/config/routes.php'; Flight::start();
ステップ5: ブログ投稿の保存と取得
投稿を読み込み、保存するメソッドを追加します。
-
投稿メソッドを追加:
index.php
で、投稿を読み込むメソッドを追加します:Flight::map('posts', function () { $file = __DIR__ . '/../data/posts.json'; return json_decode(file_get_contents($file), true); });
-
ルートの更新:
app/config/routes.php
を修正し、投稿を使用するようにします:<?php Flight::route('/', function () { $posts = Flight::posts(); Flight::view()->render('home.latte', [ 'title' => '私のブログ', 'posts' => $posts ]); }); Flight::route('/post/@slug', function ($slug) { $posts = Flight::posts(); $post = array_filter($posts, fn($p) => $p['slug'] === $slug); $post = reset($post) ?: null; if (!$post) { Flight::notFound(); return; } Flight::view()->render('post.latte', [ 'title' => $post['title'], 'post' => $post ]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => '投稿を作成']); });
ステップ6: テンプレートの作成
投稿を表示するためにテンプレートを更新します。
-
投稿ページ(
app/views/post.latte
):{extends 'layout.latte'} {block content} <h2>{$post['title']}</h2> <div class="post-content"> <p>{$post['content']}</p> </div> {/block}
ステップ7: 投稿作成の追加
新しい投稿を追加するためのフォーム送信を処理します。
-
フォーム(
app/views/create.latte
):{extends 'layout.latte'} {block content} <h2>{$title}</h2> <form method="POST" action="/create"> <div class="form-group"> <label for="title">タイトル:</label> <input type="text" name="title" id="title" required> </div> <div class="form-group"> <label for="content">コンテンツ:</label> <textarea name="content" id="content" required></textarea> </div> <button type="submit">投稿を保存</button> </form> {/block}
-
POSTルートを追加:
app/config/routes.php
で:Flight::route('POST /create', function () { $request = Flight::request(); $title = $request->data['title']; $content = $request->data['content']; $slug = strtolower(str_replace(' ', '-', $title)); $posts = Flight::posts(); $posts[] = ['slug' => $slug, 'title' => $title, 'content' => $content]; file_put_contents(__DIR__ . '/../../data/posts.json', json_encode($posts, JSON_PRETTY_PRINT)); Flight::redirect('/'); });
-
テストする:
http://localhost:8000/create
を訪問します。- 新しい投稿(例:「第二の投稿」とその内容)を送信します。
- ホームページでそれがリストされているのを確認します。
ステップ8: エラーハンドリングの強化
より良い404エクスペリエンスのためにnotFound
メソッドをオーバーライドします。
index.php
で:
Flight::map('notFound', function () {
Flight::view()->render('404.latte', ['title' => 'ページが見つかりません']);
});
app/views/404.latte
を作成します:
{extends 'layout.latte'}
{block content}
<h2>404 - {$title}</h2>
<p>申し訳ありませんが、そのページは存在しません!</p>
{/block}
次のステップ
- スタイリングの追加: より良い見た目のためにテンプレートにCSSを使用します。
- データベース:
posts.json
をSQLiteなどのデータベースに置き換えます。 - バリデーション: 重複スラッグや空の入力のチェックを追加します。
- ミドルウェア: 投稿作成のための認証を実装します。
結論
Flight PHPを使ってシンプルなブログを構築しました! このガイドでは、ルーティング、Latteによるテンプレーティング、およびフォーム送信の処理などのコア機能を示しました。すべてを軽量に保ちながら実施しています。さらにブログを進化させるためにFlightのドキュメントを探求してください!
License
The MIT License (MIT)
Copyright © 2024
@mikecao, @n0nag0n
個人が複製の許可を得ることができるように、このソフトウェアおよび関連ドキュメントファイル(以下「ソフトウェア」という)のコピーを入手することができます。 ソフトウェアを使用、コピー、変更、マージ、公開、配布、サブライセンス、販売する権利などを含む、制限なしでソフトウェアを扱う権利が、以下の条件に従って人々にそれを許可します:
上記の著作権表示およびこの許諾表示は、ソフトウェアのすべての複製または実質的な部分に含まれている必要があります。
ソフトウェアは、「現状有姿」で提供され、商品性、特定目的への適合性、および権利侵害を含むがこれに限定されない、いかなる種類の保証もなしに提供されます。 著作者または著作権保持者は、ソフトウェアまたは使用または他の取引に起因する契約上の行為、不法行為、その他の行為から生じるクレーム、損害、その他の責任について一切責任を負いません。
About
Flight とは?
Flight は、速く、シンプルで、拡張可能な PHP フレームワークです。開発者が素早く作業を完了させたい場合に最適で、一切の面倒なことを避けられます。クラシックなウェブアプリ、驚異的に速い API、または最新の AI 駆動ツールの実験など、Flight の低負荷で直感的な設計は、さまざまな用途にぴったりです。
Flight を選ぶ理由?
- 初心者向け: Flight は、PHP の新しい開発者にとって素晴らしい出発点です。明確な構造とシンプルな構文により、余計なコードに惑わされずにウェブ開発を学べます。
- プロの愛用: 経験豊富な開発者は、Flight の柔軟性と制御性に魅了されます。小規模なプロトタイプから本格的なアプリまで、スケールアップ可能で、他のフレームワークに切り替える必要はありません。
- AI 対応: Flight の最小限のオーバーヘッドとクリーンなアーキテクチャは、AI ツールや API の統合に理想的です。スマートなチャットボット、AI 駆動のダッシュボード、または単なる実験など、Flight は邪魔をせずに本質に集中できます。 AI を Flight で使用する方法について詳しく知る
クイックスタート
まず、Composer でインストールします:
composer require flightphp/core
または、リポジトリの ZIP を こちら からダウンロードできます。次に、基本的な index.php
ファイルを作成します:
<?php
// Composer でインストールした場合
require 'vendor/autoload.php';
// または ZIP ファイルで手動インストールした場合
// require 'flight/Flight.php';
Flight::route('/', function() {
echo 'hello world!';
});
Flight::route('/json', function() {
Flight::json(['hello' => 'world']);
});
Flight::start();
以上です!これで基本的な Flight アプリケーションが完成します。このファイルを php -S localhost:8000
で実行し、ブラウザで http://localhost:8000
を訪れると、出力を確認できます。
速いですか?
もちろんです! Flight は、PHP フレームワークの中でも最も速いもののひとつです。軽量なコアにより、オーバーヘッドが少なく、速度が向上します。これは、伝統的なアプリや現代の AI 駆動プロジェクトに最適です。ベンチマークは TechEmpower で確認できます。
以下は、他の人気の PHP フレームワークとのベンチマークです。
Framework | Plaintext Reqs/sec | JSON Reqs/sec |
---|---|---|
Flight | 190,421 | 182,491 |
Yii | 145,749 | 131,434 |
Fat-Free | 139,238 | 133,952 |
Slim | 89,588 | 87,348 |
Phalcon | 95,911 | 87,675 |
Symfony | 65,053 | 63,237 |
Lumen | 40,572 | 39,700 |
Laravel | 26,657 | 26,901 |
CodeIgniter | 20,628 | 19,901 |
スケルトン/ボイラープレートアプリ
Flight の開始に役立つ例のアプリがあります。flightphp/skeleton を確認して、すぐに使えるプロジェクトを入手するか、examples ページでインスピレーションを得てください。AI の統合に興味がある場合? AI 駆動の例を探す。
コミュニティ
Matrix Chat で参加できます
そして Discord も
コントリビュート
Flight に貢献する方法は 2 つあります:
- コアフレームワークに貢献する: core repository を訪れてください。
- ドキュメントを改善する! このドキュメントウェブサイトは Github でホストされています。エラーを発見したり、改善したい場合、プルリクエストを送信してください。更新や新しいアイデア、特に AI と新技術に関するものを大歓迎です!
必要条件
Flight には PHP 7.4 以上が必要です。
注意: PHP 7.4 は、2024 年現在でいくつかの LTS Linux ディストリビューションのデフォルトバージョンであるため、サポートされています。PHP >8 への移行を強制すると、ユーザーに問題を引き起こす可能性があるためです。フレームワークは PHP >8 もサポートしています。
ライセンス
Flight は MIT ライセンスで公開されています。
Awesome-plugins/php_cookie
クッキー
overclokk/cookie はアプリ内でクッキーを管理するためのシンプルなライブラリです。
インストール
composerを使用して簡単にインストールできます。
composer require overclokk/cookie
使用法
使用法は、Flightクラスに新しいメソッドを登録するだけです。
use Overclokk\Cookie\Cookie;
/*
* ブートストラップまたはpublic/index.phpファイルに設定
*/
Flight::register('cookie', Cookie::class);
/**
* ExampleController.php
*/
class ExampleController {
public function login() {
// クッキーを設定します
// インスタンスを取得するためfalseである必要があります
// オートコンプリートを有効にしたい場合は以下のコメントを使用してください
/** @var \Overclokk\Cookie\Cookie $cookie */
$cookie = Flight::cookie(false);
$cookie->set(
'stay_logged_in', // クッキーの名前
'1', // 設定したい値
86400, // クッキーの有効期間(秒)
'/', // クッキーが利用可能なパス
'example.com', // クッキーが利用可能なドメイン
true, // セキュアな HTTPS 接続でのみクッキーが送信されます
true // クッキーはHTTPプロトコルを介してのみ利用可能です
);
// オプションで、デフォルト値を維持したい場合や、
// 長期間にわたってクッキーを簡単に設定したい場合
$cookie->forever('stay_logged_in', '1');
}
public function home() {
// クッキーがあるかどうかをチェック
if (Flight::cookie()->has('stay_logged_in')) {
// 例えば、ダッシュボードエリアにリダイレクトします。
Flight::redirect('/dashboard');
}
}
}
Awesome-plugins/php_encryption
PHP 暗号化
defuse/php-encryption はデータの暗号化と復号を行うために使用できるライブラリです。すぐにデータの暗号化と復号を始めることはかなり簡単です。ライブラリの使用方法や暗号化に関連する重要なセキュリティの問題を説明する素晴らしいtutorialがあります。
インストール
composerを使用して簡単にインストールします。
composer require defuse/php-encryption
セットアップ
その後、暗号化キーを生成する必要があります。
vendor/bin/generate-defuse-key
これにより、安全に保持する必要があるキーが生成されます。キーは、ファイルの末尾にある配列内のapp/config/config.php
ファイルに保存できます。完璧な場所ではありませんが、少なくとも何かです。
使用方法
ライブラリと暗号化キーがあるので、データの暗号化と復号を開始できます。
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
/*
* ブートストラップまたはpublic/index.phpファイルに設定します
*/
// 暗号化メソッド
Flight::map('encrypt', function($raw_data) {
$encryption_key = /* $config['encryption_key']またはキーを配置した場所のfile_get_contents */;
return Crypto::encrypt($raw_data, Key::loadFromAsciiSafeString($encryption_key));
});
// 復号メソッド
Flight::map('decrypt', function($encrypted_data) {
$encryption_key = /* $config['encryption_key']またはキーを配置した場所のfile_get_contents */;
try {
$raw_data = Crypto::decrypt($encrypted_data, Key::loadFromAsciiSafeString($encryption_key));
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
// 攻撃! 間違ったキーが読み込まれたか、暗号文が作成されてから変更された可能性があります -- データベースで破損されたか、攻撃を実行しようとするEveによって意図的に変更された可能性があります。
// ... アプリケーションに適した方法でこのケースを処理します ...
}
return $raw_data;
});
Flight::route('/encrypt', function() {
$encrypted_data = Flight::encrypt('これは秘密です');
echo $encrypted_data;
});
Flight::route('/decrypt', function() {
$encrypted_data = '...'; // どこかから暗号化されたデータを取得します
$decrypted_data = Flight::decrypt($encrypted_data);
echo $decrypted_data;
});
Awesome-plugins/php_file_cache
flightphp/cache
軽量でシンプルなスタンドアロンPHPインファイルキャッシュクラス
利点
- 軽量でスタンドアロン、シンプル
- すべてのコードが1つのファイルに - 無駄なドライバーなし
- セキュア - 生成されるすべてのキャッシュファイルにはdieを含むPHPヘッダーが含まれており、パスを知っていても直接アクセスが不可能
- 良好なドキュメントとテスト済み
- flockを介して同時実行を正しく処理
- PHP 7.4+をサポート
- MITライセンスの下で無料
このドキュメントサイトは、このライブラリを使用して各ページをキャッシュしています!
コードを表示するにはこちらをクリックしてください。
インストール
composerを介してインストール:
composer require flightphp/cache
使用法
使用法は非常に簡単です。これはキャッシュディレクトリにキャッシュファイルを保存します。
use flight\Cache;
$app = Flight::app();
// キャッシュが保存されるディレクトリをコンストラクタに渡します
$app->register('cache', Cache::class, [ __DIR__ . '/../cache/' ], function(Cache $cache) {
// キャッシュはプロダクションモードのときのみ使用されることを保証します
// ENVIRONMENTはブートストラップファイルまたはアプリ内の他の場所で設定される定数です
$cache->setDevMode(ENVIRONMENT === 'development');
});
次のようにコード内で使用できます:
// キャッシュインスタンスを取得
$cache = Flight::cache();
$data = $cache->refreshIfExpired('simple-cache-test', function () {
return date("H:i:s"); // キャッシュするデータを返します
}, 10); // 10秒
// または
$data = $cache->retrieve('simple-cache-test');
if(empty($data)) {
$data = date("H:i:s");
$cache->store('simple-cache-test', $data, 10); // 10秒
}
ドキュメンテーション
完全なドキュメンテーションについてはhttps://github.com/flightphp/cacheをご覧いただき、examplesフォルダーを必ず確認してください。
Awesome-plugins/permissions
FlightPHP/Permissions
これは、アプリケーション内に複数のロールがあり、各ロールに少しずつ異なる機能がある場合にプロジェクトで使用できる権限モジュールです。このモジュールは、各ロールに対して権限を定義し、その後現在のユーザーが特定のページにアクセスする権限があるか、または特定のアクションを実行する権限があるかを確認できます。
こちらをクリックしてGitHubのリポジトリを確認してください。
インストール
composer require flightphp/permissions
を実行して、準備完了です!
使用方法
まず、権限を設定し、その後アプリケーションに権限がどういう意味なのかを伝える必要があります。最終的には、$Permissions->has()
、->can()
、またはis()
で権限を確認します。has()
と can()
には同じ機能があるため、コードをより読みやすくするために名前が異なります。
基本例
アプリケーションに、ユーザーがログインしているかどうかをチェックする機能があると仮定してください。次のように権限オブジェクトを作成できます:
// index.php
require 'vendor/autoload.php';
// 一部のコード
// おそらく誰が現在の役割であるかを示すものがあるでしょう
// 多分現在の役割を定義するセッション変数から現在の役割を取得する何かがあるでしょう、
// これはログイン後に、そうでない場合は「guest」または「public」のロールを持っています。
$current_role = 'admin';
// 権限の設定
$permission = new \flight\Permission($current_role);
$permission->defineRule('loggedIn', function($current_role) {
return $current_role !== 'guest';
});
// おそらくこのオブジェクトを Flight にある場所に持たせたいと思うでしょう
Flight::set('permission', $permission);
次に、どこかのコントローラーには、次のようなものがあるかもしれません。
<?php
// 一部のコントローラー
class SomeController {
public function someAction() {
$permission = Flight::get('permission');
if ($permission->has('loggedIn')) {
// 何かを実行
} else {
// 他の処理を実行
}
}
}
また、この機能を使用して、アプリケーション内で何かを行う権限があるかどうかを追跡することもできます。 たとえば、ソフトウェア上で投稿とやり取りできる方法がある場合、特定のアクションを実行できる権限を持っているかどうかを確認できます。
$current_role = 'admin';
// 権限の設定
$permission = new \flight\Permission($current_role);
$permission->defineRule('post', function($current_role) {
if($current_role === 'admin') {
$permissions = ['create', 'read', 'update', 'delete'];
} else if($current_role === 'editor') {
$permissions = ['create', 'read', 'update'];
} else if($current_role === 'author') {
$permissions = ['create', 'read'];
} else if($current_role === 'contributor') {
$permissions = ['create'];
} else {
$permissions = [];
}
return $permissions;
});
Flight::set('permission', $permission);
次に、どこかのコントローラーには...
class PostController {
public function create() {
$permission = Flight::get('permission');
if ($permission->can('post.create')) {
// 何かを実行
} else {
// 他の処理を実行
}
}
}
依存関係の注入
権限を定義するクロージャに依存関係を注入することができます。これは、チェックするデータポイントとしてトグル、ID、その他のデータポイントを持っている場合に便利です。同じことが Class->Method 型の呼び出しでも機能しますが、引数はメソッド内で定義します。
クロージャ
$Permission->defineRule('order', function(string $current_role, MyDependency $MyDependency = null) {
// ... コード
});
// コントローラーファイル内
public function createOrder() {
$MyDependency = Flight::myDependency();
$permission = Flight::get('permission');
if ($permission->can('order.create', $MyDependency)) {
// 何かを実行
} else {
// 他の処理を実行
}
}
クラス
namespace MyApp;
class Permissions {
public function order(string $current_role, MyDependency $MyDependency = null) {
// ... コード
}
}
クラスを使用して権限をセットするショートカット
クラスを使用して権限を定義することもできます。コードをきれいに保ちたい場合に便利です。次のように行うことができます:
<?php
// ブートストラップコード
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRule('order', 'MyApp\Permissions->order');
// myapp/Permissions.php
namespace MyApp;
class Permissions {
public function order(string $current_role, int $user_id) {
// 事前に設定したと仮定します
/** @var \flight\database\PdoWrapper $db */
$db = Flight::db();
$allowed_permissions = [ 'read' ]; // 誰でも注文を表示できます
if($current_role === 'manager') {
$allowed_permissions[] = 'create'; // マネージャーは注文を作成できます
}
$some_special_toggle_from_db = $db->fetchField('SELECT some_special_toggle FROM settings WHERE id = ?', [ $user_id ]);
if($some_special_toggle_from_db) {
$allowed_permissions[] = 'update'; // ユーザーが特別なトグルを持っている場合、注文を更新できます
}
if($current_role === 'admin') {
$allowed_permissions[] = 'delete'; // 管理者は注文を削除できます
}
return $allowed_permissions;
}
}
クールな部分は、メソッドのすべての権限を自動的にマップするショートカットもあることです(これもキャッシュされる可能性があります!!!)。したがって、order()
と company()
というメソッドがある場合、$Permissions->has('order.read')
や $Permissions->has('company.read')
を実行することができます。これらを定義することは非常に難しいので、ここで一緒にとどまります。これを行うには、次の手順を行う必要があります:
グループ化したい権限クラスを作成します。
class MyPermissions {
public function order(string $current_role, int $order_id = 0): array {
// 権限を決定するためのコード
return $permissions_array;
}
public function company(string $current_role, int $company_id): array {
// 権限を決定するためのコード
return $permissions_array;
}
}
次に、このライブラリを使用して権限を検出できるようにします。
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class);
Flight::set('permissions', $Permissions);
最後に、コードベースで権限を呼び出して、ユーザーが与えられた権限を実行できるかどうかを確認します。
class SomeController {
public function createOrder() {
if(Flight::get('permissions')->can('order.create') === false) {
die('You can\'t create an order. Sorry!');
}
}
}
キャッシュ
キャッシュを有効にするには、単純なwruczak/phpfilecacheライブラリを参照してください。これを有効にする例は以下の通りです。
// この $app はあなたのコードの一部である可能性があり、
// コンストラクター内で Flight::app() から取得されるか
// null を渡すと、それがコンストラクター内で取得されます
$app = Flight::app();
// 現時点では、ファイルキャッシュとしてこれを受け入れます。今後他のものも簡単に追加できます。
$Cache = new Wruczek\PhpFileCache\PhpFileCache;
$Permissions = new \flight\Permission($current_role, $app, $Cache);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class, 3600); // キャッシュする秒数。キャッシュを使用しない場合はこれをオフにしてください
Awesome-plugins/simple_job_queue
シンプルジョブキュー
シンプルジョブキューは、非同期でジョブを処理するために使用できるライブラリです。beanstalkd、MySQL/MariaDB、SQLite、およびPostgreSQLで使用できます。
インストール
composer require n0nag0n/simple-job-queue
使用法
これを機能させるには、キューにジョブを追加する方法と、ジョブを処理する方法(ワーカー)が必要です。以下は、ジョブをキューに追加する方法と、そのジョブを処理する方法の例です。
Flightへの追加
これをFlightに追加するのは簡単で、register()
メソッドを使用して行います。以下は、これをFlightに追加する方法の例です。
<?php
require 'vendor/autoload.php';
// beanstalkdを使用する場合は、['mysql']を['beanstalkd']に変更してください
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
// Flight::db()で既にPDO接続がある場合
$Job_Queue->addQueueConnection(Flight::db());
// または、beanstalkd/Pheanstalkを使用している場合
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
});
新しいジョブの追加
ジョブを追加する場合、パイプライン(キュー)を指定する必要があります。これは、RabbitMQのチャネルやbeanstalkdのチューブに相当します。
<?php
Flight::queue()->selectPipeline('send_important_emails');
Flight::queue()->addJob(json_encode([ 'something' => 'that', 'ends' => 'up', 'a' => 'string' ]));
ワーカーの実行
ここにワーカーを実行する方法のサンプルファイルがあります。
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// PDO接続
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'user', 'pass');
$Job_Queue->addQueueConnection($PDO);
// または、beanstalkd/Pheanstalkを使用している場合
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
$Job_Queue->watchPipeline('send_important_emails');
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
// あなたが夜に良く眠れるように調整してください(データベースキューのみ、beanstalkdではこのif文は必要ありません)
if(empty($job)) {
usleep(500000);
continue;
}
echo "処理中 {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
} else {
// これはレディキューから取り出し、後で拾って「キック」できる別のキューに入れます。
$Job_Queue->buryJob($job);
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
}
}
Supervisordを使用した長いプロセスの処理
Supervisordは、ワーカープロセスが継続的に実行されることを保証するプロセス制御システムです。シンプルジョブキューワーカーの設定に関するより完全なガイドは次のとおりです。
Supervisordのインストール
# Ubuntu/Debian上
sudo apt-get install supervisor
# CentOS/RHEL上
sudo yum install supervisor
# Homebrewを使用したmacOS上
brew install supervisor
ワーカースクリプトの作成
最初に、ワーカーコードを専用のPHPファイルに保存します。
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// PDO接続
$PDO = new PDO('mysql:dbname=your_database;host=127.0.0.1', 'username', 'password');
$Job_Queue->addQueueConnection($PDO);
// 監視するパイプラインを設定
$Job_Queue->watchPipeline('send_important_emails');
// ワーカーの開始をログに記録
echo date('Y-m-d H:i:s') . " - ワーカーが開始されました\n";
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
if(empty($job)) {
usleep(500000); // 0.5秒間スリープ
continue;
}
echo date('Y-m-d H:i:s') . " - ジョブ {$job['id']}を処理中\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
echo date('Y-m-d H:i:s') . " - ジョブ {$job['id']}は正常に完了しました\n";
} else {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - ジョブ {$job['id']}が失敗し、埋められました\n";
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - ジョブ {$job['id']}の処理中に例外が発生しました: {$e->getMessage()}\n";
}
}
Supervisordの設定
ワーカーのための設定ファイルを作成します。
[program:email_worker]
command=php /path/to/worker.php
directory=/path/to/project
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/simple_job_queue_err.log
stdout_logfile=/var/log/simple_job_queue.log
user=www-data
numprocs=2
process_name=%(program_name)s_%(process_num)02d
主要な設定オプション:
command
: ワーカーを実行するコマンドdirectory
: ワーカーの作業ディレクトリautostart
: supervisordが起動するときに自動的に開始autorestart
: プロセスが終了した場合に自動的に再起動startretries
: 失敗した場合に再起動を試みる回数stderr_logfile
/stdout_logfile
: ログファイルの場所user
: プロセスを実行するシステムユーザーnumprocs
: 実行するワーカーインスタンスの数process_name
: 複数のワーカープロセスの命名形式
Supervisorctlによるワーカーの管理
設定を作成または変更した後:
# supervisor設定を再読み込み
sudo supervisorctl reread
sudo supervisorctl update
# 特定のワーカープロセスを制御
sudo supervisorctl start email_worker:*
sudo supervisorctl stop email_worker:*
sudo supervisorctl restart email_worker:*
sudo supervisorctl status email_worker:*
複数のパイプラインの実行
複数のパイプラインの場合、別々のワーカーファイルと設定を作成します。
[program:email_worker]
command=php /path/to/email_worker.php
# ... その他の設定 ...
[program:notification_worker]
command=php /path/to/notification_worker.php
# ... その他の設定 ...
監視とログ
ワーカーのアクティビティを監視するために、ログを確認します。
# ログを表示
sudo tail -f /var/log/simple_job_queue.log
# ステータスを確認
sudo supervisorctl status
この設定により、ジョブワーカーはクラッシュ、サーバーの再起動、またはその他の問題が発生しても継続的に実行されることが保証され、プロダクション環境におけるキューシステムの信頼性が高まります。
Awesome-plugins/index
すばらしいプラグイン
Flightは非常に拡張可能です。Flightアプリケーションに機能を追加するために使用できるプラグインがいくつかあります。いくつかはFlightチームによって公式にサポートされており、他には開始を手助けするためのミクロ/ライトライブラリがあります。
キャッシュ
キャッシュはアプリケーションの高速化に役立つ方法です。Flightと使用できるキャッシュライブラリがいくつかあります。
- Wruczek/PHP-File-Cache - 軽量でシンプルなPHPファイル内キャッシュクラス
デバッグ
開発を行うローカル環境ではデバッグが重要です。いくつかのプラグインを使用するとデバッグ体験を向上させることができます。
- tracy/tracy - Flightと使用できるフル機能のエラーハンドラ。アプリケーションのデバッグに役立ついくつかのパネルがあります。また、容易に拡張して独自のパネルを追加できます。
- flightphp/tracy-extensions - Tracy エラーハンドラと共に使用し、Flightプロジェクトのデバッグを支援する追加パネルが含まれています。
データベース
データベースはほとんどのアプリケーションの中心です。これによりデータの保存と取得が可能になります。一部のデータベースライブラリはクエリの記述や実行を簡素化するラッパーであり、一部は完全なORMです。
- flightphp/core PdoWrapper - Flightの公式PDOラッパーで、コアの一部です。クエリの記述と実行のプロセスを簡単にするためのシンプルなラッパーです。ORMではありません。
- flightphp/active-record - Flightの公式ActiveRecord ORM/Mapper。データの簡単な取得と保存に適した優れたライブラリ。
セッション
APIにはあまり役立たないが、Webアプリケーションの構築にはセッションが状態とログイン情報の維持に重要です。
- Ghostff/Session - PHPセッションマネージャー(非同期、フラッシュ、セグメント、セッション暗号化)。セッションデータの暗号化/復号化のためにPHP open_sslを使用します。
テンプレーティング
テンプレートはUIを持つWebアプリケーションにとって重要です。Flightと使用できるいくつかのテンプレートエンジンがあります。
- flightphp/core View - コアの一部である非常に基本的なテンプレートエンジンです。プロジェクト内に複数のページがある場合は推奨されません。
- latte/latte - PHP構文に近い感覚で非常に使いやすい完全機能のテンプレートエンジンです。TwigやSmartyよりも簡単に拡張して独自のフィルターや関数を追加できます。
貢献
共有したいプラグインがありますか?リストに追加するためにプルリクエストを送信してください!
Awesome-plugins/ghost_session
Ghostff/Session
PHP セッションマネージャー(非ブロッキング、フラッシュ、セグメント、セッション暗号化)。PHP open_ssl を使用してセッション データのオプションの暗号化/復号化をサポートします。File, MySQL, Redis, and Memcached をサポートします。
こちらをクリックしてコードを表示します。
インストール
Composer でインストールします。
composer require ghostff/session
基本的な構成
デフォルトの設定を使用するには何も渡す必要はありません。詳細な設定については、Github Readmeを参照してください。
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
// 各ページの読み込みでセッションをコミットする必要がありますことを覚えておいてください
// または、構成で auto_commit を実行する必要があります。
簡単な例
これがこの使用方法の簡単な例です。
Flight::route('POST /login', function() {
$session = Flight::session();
// ここでログインのロジックを実行します
// パスワードを検証するなど。
// ログインに成功した場合
$session->set('is_logged_in', true);
$session->set('user', $user);
// セッションに書き込んだら、意図的にコミットする必要があります。
$session->commit();
});
// このチェックは制限されたページのロジックで実行するか、ミドルウェアでラップできます。
Flight::route('/some-restricted-page', function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
// ここで制限されたページのロジックを実行します
});
// ミドルウェア版
Flight::route('/some-restricted-page', function() {
// 通常のページロジック
})->addMiddleware(function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
});
より複雑な例
これがこの使用方法のより複雑な例です。
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
// 最初の引数としてセッション構成ファイルのカスタムパスを設定します
// または、カスタム配列を与えます
$app->register('session', Session::class, [
[
// セッション データをデータベースに保存したい場合(例: 「すべてのデバイスからログアウト」機能)
Session::CONFIG_DRIVER => Ghostff\Session\Drivers\MySql::class,
Session::CONFIG_ENCRYPT_DATA => true,
Session::CONFIG_SALT_KEY => hash('sha256', 'my-super-S3CR3T-salt'), // これは別のものに変更してください
Session::CONFIG_AUTO_COMMIT => true, // これは必要で、commit() が難しい場合のみ実行してください。
// さらに、Flight::after('start', function() { Flight::session()->commit(); }); を実行できます。
Session::CONFIG_MYSQL_DS => [
'driver' => 'mysql', # Database driver for PDO dns eg(mysql:host=...;dbname=...)
'host' => '127.0.0.1', # Database host
'db_name' => 'my_app_database', # Database name
'db_table' => 'sessions', # Database table
'db_user' => 'root', # Database username
'db_pass' => '', # Database password
'persistent_conn'=> false, # スクリプトがデータベースにアクセスするたびに新しい接続を確立するオーバーヘッドを避ける。詳細は自分で確認してください
]
]
]);
助けて! 私のセッションデータが持続しません!
セッションデータを設定してもリクエスト間で持続しない場合、セッションデータをコミットすることを忘れている可能性があります。$session->commit() を呼び出して設定した後でこれを実行してください。
Flight::route('POST /login', function() {
$session = Flight::session();
// ここでログインのロジックを実行します
// パスワードを検証するなど。
// ログインに成功した場合
$session->set('is_logged_in', true);
$session->set('user', $user);
// セッションに書き込んだら、意図的にコミットする必要があります。
$session->commit();
});
これを回避する方法として、セッション サービスを設定するときに構成で auto_commit
を true
に設定します。これにより、各リクエスト後にセッションデータが自動的にコミットされます。
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
$session->updateConfiguration([
Session::CONFIG_AUTO_COMMIT => true,
]);
}
);
さらに、Flight::after('start', function() { Flight::session()->commit(); });
を実行して各リクエスト後にセッションデータをコミットすることもできます。
ドキュメント
完全なドキュメントについては、Github Readmeを訪問してください。構成オプションはdefault_config.php ファイル自体でよく文書化されています。コードは自分で確認したい場合に簡単に理解できます。
Awesome-plugins/pdo_wrapper
PdoWrapper PDO ヘルパークラス
Flight には PDO 用のヘルパークラスが付属しています。これにより、準備/実行/fetchAll() の混乱を簡単にクエリすることができます。データベースのクエリが大幅に簡素化されます。各行の結果は Flight Collection クラスとして返され、配列構文またはオブジェクト構文を使用してデータにアクセスできます。
PDO ヘルパークラスの登録
// PDO ヘルパークラスを登録します
Flight::register('db', \flight\database\PdoWrapper::class, ['mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'utf8mb4\'',
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
]);
使用法
このオブジェクトは PDO を拡張しているため、通常の PDO メソッドをすべて使用できます。データベースのクエリをより簡単にするために次のメソッドが追加されています:
runQuery(string $sql, array $params = []): PDOStatement
INSERT や UPDATE に使用したり、SELECT を while ループで使用する予定がある場合に使用します。
$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM table WHERE something = ?", [ $something ]);
while($row = $statement->fetch()) {
// ...
}
// またはデータベースに書き込む場合
$db->runQuery("INSERT INTO table (name) VALUES (?)", [ $name ]);
$db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]);
fetchField(string $sql, array $params = []): mixed
クエリから最初のフィールドを取得します
$db = Flight::db();
$count = $db->fetchField("SELECT COUNT(*) FROM table WHERE something = ?", [ $something ]);
fetchRow(string $sql, array $params = []): array
クエリから1行を取得します
$db = Flight::db();
$row = $db->fetchRow("SELECT id, name FROM table WHERE id = ?", [ $id ]);
echo $row['name'];
// または
echo $row->name;
fetchAll(string $sql, array $params = []): array
クエリからすべての行を取得します
$db = Flight::db();
$rows = $db->fetchAll("SELECT id, name FROM table WHERE something = ?", [ $something ]);
foreach($rows as $row) {
echo $row['name'];
// または
echo $row->name;
}
IN()
構文について
IN()
ステートメント用の便利なラッパーもあります。IN()
のプレースホルダーとして単一のクエスチョンマークを渡し、その後に値の配列を単純に渡すことができます。以下はその例です:
$db = Flight::db();
$name = 'Bob';
$company_ids = [1,2,3,4,5];
$rows = $db->fetchAll("SELECT id, name FROM table WHERE name = ? AND company_id IN (?)", [ $name, $company_ids ]);
完全な例
// 例として、このラッパーを使用する方法とルートを示します
Flight::route('/users', function () {
// すべてのユーザーを取得
$users = Flight::db()->fetchAll('SELECT * FROM users');
// すべてのユーザーを表示
$statement = Flight::db()->runQuery('SELECT * FROM users');
while ($user = $statement->fetch()) {
echo $user['name'];
// または echo $user->name;
}
// 単一のユーザーを取得
$user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);
// 単一の値を取得
$count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');
// 進行補助として IN() 構文を使用します (IN が大文字であることを確認してください)
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);
// またはこのようにもできます
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [ '1,2,3,4,5']);
// 新しいユーザーを挿入します
Flight::db()->runQuery("INSERT INTO users (name, email) VALUES (?, ?)", ['Bob', 'bob@example.com']);
$insert_id = Flight::db()->lastInsertId();
// ユーザーを更新します
Flight::db()->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 123]);
// ユーザーを削除します
Flight::db()->runQuery("DELETE FROM users WHERE id = ?", [123]);
// 影響を受けた行数を取得します
$statement = Flight::db()->runQuery("UPDATE users SET name = ? WHERE name = ?", ['Bob', 'Sally']);
$affected_rows = $statement->rowCount();
});
Awesome-plugins/migrations
マイグレーション
プロジェクトのマイグレーションは、プロジェクトに関与するすべてのデータベース変更を追跡します。 byjg/php-migration は、あなたが始めるのに非常に役立つコアライブラリです。
インストール
PHP ライブラリ
プロジェクトで PHP ライブラリのみを使用したい場合:
composer require "byjg/migration"
コマンドラインインターフェース
コマンドラインインターフェースはスタンドアロンであり、プロジェクトにインストールする必要はありません。
グローバルにインストールし、シンボリックリンクを作成できます。
composer require "byjg/migration-cli"
マイグレーション CLI に関する詳細情報は、byjg/migration-cliをご覧ください。
サポートされているデータベース
データベース | ドライバー | 接続文字列 |
---|---|---|
Sqlite | pdo_sqlite | sqlite:///path/to/file |
MySql/MariaDb | pdo_mysql | mysql://username:password@hostname:port/database |
Postgres | pdo_pgsql | pgsql://username:password@hostname:port/database |
Sql Server | pdo_dblib, pdo_sysbase Linux | dblib://username:password@hostname:port/database |
Sql Server | pdo_sqlsrv Windows | sqlsrv://username:password@hostname:port/database |
どのように機能しますか?
データベースマイグレーションは、データベースのバージョン管理に純粋な SQL を使用します。 機能させるためには、次のことを行う必要があります。
- SQL スクリプトを作成する
- コマンドラインまたは API を使用して管理する。
SQL スクリプト
スクリプトは、3 つのセットのスクリプトに分かれています。
- BASE スクリプトは、新しいデータベースを作成するためのすべての SQL コマンドを含みます。
- UP スクリプトは、データベースのバージョンを "up" するためのすべての SQL マイグレーションコマンドを含みます。
- DOWN スクリプトは、データベースのバージョンを "down" するためのすべての SQL マイグレーションコマンドを含みます。
スクリプトディレクトリは次のとおりです:
<root dir>
|
+-- base.sql
|
+-- /migrations
|
+-- /up
|
+-- 00001.sql
+-- 00002.sql
+-- /down
|
+-- 00000.sql
+-- 00001.sql
- "base.sql" はベーススクリプトです
- "up" フォルダーには、バージョンをアップするためのスクリプトが含まれています。 例えば: 00002.sql は、データベースをバージョン '1' から '2' へ移動させるためのスクリプトです。
- "down" フォルダーには、バージョンをダウンするためのスクリプトが含まれています。 例えば: 00001.sql は、データベースをバージョン '2' から '1' へ移動させるためのスクリプトです。 "down" フォルダーはオプションです。
マルチ開発環境
複数の開発者や複数のブランチで作業する場合、次の番号を特定するのは難しいです。
その場合、バージョン番号の後にサフィックス "-dev" を付けます。
シナリオを見てみましょう:
- 開発者 1 がブランチを作成し、最新のバージョンが e.g. 42 です。
- 開発者 2 が同時にブランチを作成し、同じデータベースバージョン番号を持っています。
どちらの場合も、開発者は 43-dev.sql というファイルを作成します。 両方の開発者は UP と DOWN を問題なく移行し、あなたのローカルバージョンは 43 になります。
しかし、開発者 1 が変更をマージし、最終バージョン 43.sql を作成しました(git mv 43-dev.sql 43.sql
)。 開発者 2 がローカルブランチを更新すると、彼はファイル 43.sql(dev 1 から)とファイル 43-dev.sql を持ちます。
彼が UP または DOWN に移行しようとすると、マイグレーションスクリプトはダウンし、バージョンが 43 の 2 つが存在することを警告します。その場合、開発者 2 はファイルを 44-dev.sql に更新し、変更をマージして最終バージョンを生成するまで作業を続行しなければなりません。
PHP API を使用してプロジェクトに統合する
基本的な使用法は
- 接続管理オブジェクトの接続を作成する。詳細については、"byjg/anydataset" コンポーネントを参照してください。
- この接続と SQL スクリプトがあるフォルダーを使用してマイグレーションオブジェクトを作成します。
- マイグレーションスクリプトを "reset"、"up" または "down" のための適切なコマンドを使用します。
例えばを見る:
<?php
// 接続 URI を作成
// 詳細: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');
// データベースまたはデータベースをその URI で処理するように登録します:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// マイグレーションインスタンスを作成する
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// 実行からの情報を受け取るためにコールバック進捗関数を追加します
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
echo "$action, $currentVersion, ${fileInfo['description']}\n";
});
// "base.sql" スクリプトを使用してデータベースを復元し
// データベースのバージョンを最新バージョンまでアップグレードするためのすべてのスクリプトを実行します
$migration->reset();
// 現在のバージョンから $version 番号までのデータベースのバージョンのためのすべてのスクリプトを実行します;
// バージョン番号が指定されていない場合、最後のデータベースバージョンまで移行します
$migration->update($version = null);
マイグレーションオブジェクトは、データベースのバージョンを制御します。
プロジェクト内のバージョン管理作成
<?php
// データベースまたはデータベースをその URI で処理するように登録します:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// マイグレーションインスタンスを作成する
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// このコマンドは、データベース内にバージョンテーブルを作成します
$migration->createVersion();
現在のバージョンの取得
<?php
$migration->getCurrentVersion();
コールバックを追加して進捗を制御
<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
echo "コマンドを実行中: $command バージョン $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});
Db ドライバーインスタンスの取得
<?php
$migration->getDbDriver();
使用するには、次を訪れてください: https://github.com/byjg/anydataset-db
部分的なマイグレーションを避ける(MySQL では利用できません)
部分的なマイグレーションは、エラーや手動の中断によりマイグレーションスクリプトがプロセスの途中で中断される場合です。
マイグレーションテーブルは partial up
または partial down
の状態になり、再度移行できるようになる前に手動で修正する必要があります。
この状況を避けるために、マイグレーションがトランザクショナルコンテキストで実行されることを指定できます。
マイグレーションスクリプトが失敗すると、トランザクションはロールバックされ、マイグレーションテーブルは complete
とマークされ、
バージョンはエラーを引き起こしたスクリプトの直前のバージョンになります。
この機能を有効にするには、withTransactionEnabled
メソッドを呼び出して、true
をパラメータとして渡す必要があります:
<?php
$migration->withTransactionEnabled(true);
注: この機能は、MySQL では利用できません。DDL コマンドをトランザクション内でサポートしていないためです。 このメソッドを MySQL で使用した場合、マイグレーションは静かに無視します。 詳細情報: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
Postgres 用の SQL マイグレーションを書く際のヒント
トリガーと SQL 関数の作成時
-- DO
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- empname と salary が指定されていることを確認します
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null'; -- これらのコメントが空でも関係ありません
END IF; --
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname; --
END IF; --
-- 誰が私たちのために働いているのか、彼らはそれのために支払わなければなりませんか?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname; --
END IF; --
-- 誰が給与を変更したのかを記憶して
NEW.last_date := current_timestamp; --
NEW.last_user := current_user; --
RETURN NEW; --
END; --
$emp_stamp$ LANGUAGE plpgsql;
-- DON'T
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- empname と salary が指定されていることを確認します
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname cannot be null';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% cannot have null salary', NEW.empname;
END IF;
-- 誰が私たちのために働いているのか、彼らはそれのために支払わなければなりませんか?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
END IF;
-- 誰が給与を変更したのかを記憶して
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
PDO
データベース抽象層は SQL ステートメントのバッチを実行できないため、byjg/migration
がマイグレーションファイルを読み込むと、ファイルの内容全体をセミコロンで分割し、ステートメントを個別に実行する必要があります。ただし、1 つの種類のステートメントはその本体の間に複数のセミコロンを持つことがあります: 関数です。
関数を正しく解析できるようにするために、byjg/migration
2.1.0 からマイグレーションファイルを セミコロン + EOL
シーケンスで分割するようになりました。これにより、関数定義の各内部セミコロンの後に空のコメントを追加すると、byjg/migration
がそれを解析できるようになります。
不幸にも、これらのコメントのいずれかを追加するのを忘れると、ライブラリは CREATE FUNCTION
ステートメントを複数の部分に分割し、マイグレーションは失敗します。
コロン文字(:
)を避ける
-- DO
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (CAST(booked_at AS DATE) <= check_in),
check_in DATE NOT NULL
);
-- DON'T
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (booked_at::DATE <= check_in),
check_in DATE NOT NULL
);
PDO
は名前付きパラメータのプレフィックスとしてコロン文字を使用するため、他のコンテキストでの使用は問題を引き起こします。
例えば、PostgreSQL ステートメントは ::
を使用して型の間で値をキャストできます。 一方で、PDO
はこれを無効な名前付きパラメータとして解釈し、無効なコンテキストで失敗します。
この不一致を修正する唯一の方法は、コロンを完全に避けることです(この場合、PostgreSQL にも代替構文があります: CAST(value AS type)
)。
SQL エディタを使用する
最後に、手動で SQL マイグレーションを書くことは面倒ですが、SQL 構文を理解し、オートコンプリートを提供し、現在のデータベーススキーマを調査しているエディタを使用すれば、格段に簡単になります。
1 つのスキーマ内での異なるマイグレーションの処理
同じスキーマ内で異なるマイグレーションスクリプトやバージョンを作成する必要がある場合、可能ですがリスクが高く、私は 全く推奨しません。
これを行うには、コンストラクタにパラメータを渡して異なる "マイグレーションテーブル" を作成する必要があります。
<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "NEW_MIGRATION_TABLE_NAME");
セキュリティ上の理由から、この機能はコマンドラインでは利用できませんが、環境変数 MIGRATION_VERSION
を使用して名前を保存できます。
この機能を使用しないことを強く推奨します。推奨は、1 つのスキーマにつき 1 つのマイグレーションです。
ユニットテストの実行
基本的なユニットテストは次のように実行できます:
vendor/bin/phpunit
データベーステストの実行
統合テストを実行するには、データベースを立ち上げておく必要があります。基本的な docker-compose.yml
を提供しており、テストのためにデータベースを起動する際に使用できます。
データベースの実行
docker-compose up -d postgres mysql mssql
テストを実行
vendor/bin/phpunit
vendor/bin/phpunit tests/SqliteDatabase*
vendor/bin/phpunit tests/MysqlDatabase*
vendor/bin/phpunit tests/PostgresDatabase*
vendor/bin/phpunit tests/SqlServerDblibDatabase*
vendor/bin/phpunit tests/SqlServerSqlsrvDatabase*
オプションとして、ユニットテストで使用されるホストとパスワードを設定できます。
export MYSQL_TEST_HOST=localhost # デフォルトは localhost
export MYSQL_PASSWORD=newpassword # null パスワードが必要な場合は '.' を使用
export PSQL_TEST_HOST=localhost # デフォルトは localhost
export PSQL_PASSWORD=newpassword # null パスワードが必要な場合は '.' を使用
export MSSQL_TEST_HOST=localhost # デフォルトは localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db # デフォルトは /tmp/test.db
Awesome-plugins/session
FlightPHP セッション - 軽量なファイルベースのセッション ハンドラ
これは、Flight PHP Framework 向けの軽量でファイルベースのセッション ハンドラ プラグインです。ノンブロッキングのセッション読み込み、オプションの暗号化、オートコミット機能、開発用のテスト モードなどの機能を提供し、セッション管理を簡単かつ強力にします。セッション データはファイルに保存されるため、データベースを必要としないアプリケーションに理想的です。
データベースを使用したい場合は、同じ機能の多くを持つがデータベース バックエンドを備えた ghostff/session プラグインを参照してください。
完全なソース コードと詳細については、Github リポジトリを訪問してください。
インストール
Composer を介してプラグインをインストールします:
composer require flightphp/session
基本的な使用方法
Flight アプリケーションで flightphp/session
プラグインを使用する簡単な例です:
require 'vendor/autoload.php';
use flight\Session;
$app = Flight::app();
// セッション サービスを登録
$app->register('session', Session::class);
// セッションを使用した例のルート
Flight::route('/login', function() {
$session = Flight::session();
$session->set('user_id', 123);
$session->set('username', 'johndoe');
$session->set('is_admin', false);
echo $session->get('username'); // 出力: johndoe
echo $session->get('preferences', 'default_theme'); // 出力: default_theme
if ($session->get('user_id')) {
Flight::json(['message' => 'User is logged in!', 'user_id' => $session->get('user_id')]);
}
});
Flight::route('/logout', function() {
$session = Flight::session();
$session->clear(); // すべてのセッション データをクリア
Flight::json(['message' => 'Logged out successfully']);
});
Flight::start();
重要なポイント
- Non-Blocking: デフォルトで
read_and_close
を使用し、セッション ロックの問題を防ぎます。 - Auto-Commit: デフォルトで有効なので、シャットダウン時に変更が自動的に保存されますが、無効にすることもできます。
- File Storage: セッションはデフォルトでシステムの temp ディレクトリの下の
/flight_sessions
に保存されます。
構成
登録時にオプションの配列を渡すことで、セッション ハンドラをカスタマイズできます:
// はい、二重配列です :)
$app->register('session', Session::class, [ [
'save_path' => '/custom/path/to/sessions', // セッション ファイルのディレクトリ
'prefix' => 'myapp_', // セッション ファイルのプレフィックス
'encryption_key' => 'a-secure-32-byte-key-here', // 暗号化を有効にする (AES-256-CBC のために 32 バイト推奨)
'auto_commit' => false, // オートコミットを無効にして手動制御
'start_session' => true, // 自動的にセッションを開始 (デフォルト: true)
'test_mode' => false, // 開発用のテスト モードを有効
'serialization' => 'json', // シリアル化方法: 'json' (デフォルト) または 'php' (レガシー)
] ]);
構成オプション
Option | Description | Default Value |
---|---|---|
save_path |
セッション ファイルが保存されるディレクトリ | sys_get_temp_dir() . '/flight_sessions' |
prefix |
保存されたセッション ファイルのプレフィックス | sess_ |
encryption_key |
AES-256-CBC 暗号化のためのキー (オプション) | null (暗号化なし) |
auto_commit |
シャットダウン時にセッション データを自動保存 | true |
start_session |
自動的にセッションを開始 | true |
test_mode |
PHP セッションに影響を与えないテスト モードで実行 | false |
test_session_id |
テスト モード用のカスタム セッション ID (オプション) | 設定されていない場合ランダム生成 |
serialization |
シリアル化方法: 'json' (デフォルト、安全) または 'php' (レガシー、オブジェクトを許可) | 'json' |
シリアル化モード
このライブラリはデフォルトで JSON シリアル化 を使用し、セッション データの安全性が高く、PHP オブジェクト注入の脆弱性を防ぎます。セッションに PHP オブジェクトを保存する必要がある場合 (ほとんどのアプリでは推奨されません) は、レガシーの PHP シリアル化を選択できます:
'serialization' => 'json'
(デフォルト):- セッション データに配列とプリミティブのみを許可。
- より安全: PHP オブジェクト注入に耐性あり。
- ファイルは
J
(プレーン JSON) またはF
(暗号化 JSON) でプレフィックス付け。
'serialization' => 'php'
:- PHP オブジェクトの保存を許可 (注意して使用)。
- ファイルは
P
(プレーン PHP シリアル化) またはE
(暗号化 PHP シリアル化) でプレフィックス付け。
注: JSON シリアル化を使用している場合、オブジェクトを保存しようとすると例外が発生します。
高度な使用方法
手動コミット
オートコミットを無効にした場合、変更を手動でコミットする必要があります:
$app->register('session', Session::class, ['auto_commit' => false]);
Flight::route('/update', function() {
$session = Flight::session();
$session->set('key', 'value');
$session->commit(); // 変更を明示的に保存
});
暗号化によるセッション セキュリティ
機密データを保護するために暗号化を有効にします:
$app->register('session', Session::class, [
'encryption_key' => 'your-32-byte-secret-key-here'
]);
Flight::route('/secure', function() {
$session = Flight::session();
$session->set('credit_card', '4111-1111-1111-1111'); // 自動的に暗号化
echo $session->get('credit_card'); // 取得時に復号化
});
セッション ID の再生成
セキュリティのために (例: ログイン後) セッション ID を再生成します:
Flight::route('/post-login', function() {
$session = Flight::session();
$session->regenerate(); // 新しい ID、データを保持
// または
$session->regenerate(true); // 新しい ID、古いデータを削除
});
ミドルウェアの例
セッション ベースの認証でルートを保護します:
Flight::route('/admin', function() {
Flight::json(['message' => 'Welcome to the admin panel']);
})->addMiddleware(function() {
$session = Flight::session();
if (!$session->get('is_admin')) {
Flight::halt(403, 'Access denied');
}
});
これはミドルウェアでの簡単な例です。より詳細な例については、middleware ドキュメントを参照してください。
メソッド
Session
クラスは以下のメソッドを提供します:
set(string $key, $value)
: セッションに値を保存。get(string $key, $default = null)
: 値を取得し、キーが存在しない場合にデフォルト値をオプションで指定。delete(string $key)
: 特定のキーをセッションから削除。clear()
: すべてのセッション データを削除しますが、同じファイル名を保持。commit()
: 現在のセッション データをファイル システムに保存。id()
: 現在のセッション ID を返します。regenerate(bool $deleteOldFile = false)
: セッション ID を再生成し、新しいセッション ファイルを作成します。古いデータを保持し、古いファイルはシステムに残ります。$deleteOldFile
がtrue
の場合、古いセッション ファイルを削除。destroy(string $id)
: 指定された ID のセッションを破棄し、セッション ファイルをシステムから削除します。これはSessionHandlerInterface
の一部で、$id
は必須です。典型的な使用例は$session->destroy($session->id())
です。getAll()
: 現在のセッションのすべてのデータを返します。
get()
と id()
を除くすべてのメソッドは、チェイニングのために Session
インスタンスを返します。
このプラグインを使う理由
- Lightweight: 外部依存なし—just ファイルのみ。
- Non-Blocking: デフォルトで
read_and_close
を使用してセッション ロックを回避。 - Secure: 機密データ用の AES-256-CBC 暗号化をサポート。
- Flexible: オートコミット、テスト モード、手動制御のオプション。
- Flight-Native: Flight フレームワーク専用に構築。
技術詳細
- Storage Format: セッション ファイルは構成された
save_path
にsess_
でプレフィックス付けされて保存されます。ファイル コンテンツのプレフィックス:J
: プレーン JSON (デフォルト、暗号化なし)F
: 暗号化 JSON (デフォルト、暗号化あり)P
: プレーン PHP シリアル化 (レガシー、暗号化なし)E
: 暗号化 PHP シリアル化 (レガシー、暗号化あり)
- Encryption:
encryption_key
が提供された場合、各セッション 書き込みごとにランダム IV を使用して AES-256-CBC を適用。JSON と PHP シリアル化の両方で動作。 - Serialization: JSON がデフォルトで最も安全。PHP シリアル化はレガシー/高度な使用のために利用可能ですが、セキュリティが低い。
- Garbage Collection: 期限切れのセッションをクリーンアップするための PHP の
SessionHandlerInterface::gc()
を実装。
貢献
貢献を歓迎します! リポジトリ をフォークし、変更を加えてプル リクエストを送信してください。バグの報告や機能の提案は Github のイシュー トラッカーで行ってください。
ライセンス
このプラグインは MIT ライセンスの下でライセンスされています。詳細は Github リポジトリ を参照してください。
Awesome-plugins/runway
ランウェイ
ランウェイはCLIアプリケーションで、Flightアプリケーションの管理を支援します。コントローラを生成したり、すべてのルートを表示したりすることができます。優れたadhocore/php-cliライブラリに基づいています。
こちらをクリックして、コードを表示してください。
インストール
Composerを使用してインストールしてください。
composer require flightphp/runway
基本設定
ランウェイを実行する最初の回は、セットアッププロセスを進め、プロジェクトのルートに.runway.json
構成ファイルを作成します。このファイルには、ランウェイが正しく動作するために必要ないくつかの構成が含まれています。
使用法
ランウェイには、Flightアプリケーションを管理するために使用できる複数のコマンドがあります。ランウェイを使用する方法は2つあります。
- スケルトンプロジェクトを使用している場合、プロジェクトのルートから
php runway [command]
を実行できます。 - Composerを介してインストールされたパッケージとしてRunwayを使用している場合、プロジェクトのルートから
vendor/bin/runway [command]
を実行できます。
任意のコマンドに対して、--help
フラグを渡すと、そのコマンドの使用方法に関するより詳細な情報を取得できます。
php runway routes --help
以下はいくつかの例です。
コントローラを生成する
.runway.json
ファイルの構成に基づいて、デフォルトの場所は app/controllers/
ディレクトリにコントローラを生成します。
php runway make:controller MyController
アクティブレコードモデルを生成する
.runway.json
ファイルの構成に基づいて、デフォルトの場所は app/records/
ディレクトリにコントローラを生成します。
php runway make:record users
たとえば、次のスキーマを持つ users
テーブルがある場合:id
、name
、email
、created_at
、updated_at
、app/records/UserRecord.php
ファイルに類似したファイルが作成されます:
<?php
declare(strict_types=1);
namespace app\records;
/**
* ユーザーテーブルのアクティブレコードクラス。
* @link https://docs.flightphp.com/awesome-plugins/active-record
*
* @property int $id
* @property string $name
* @property string $email
* @property string $created_at
* @property string $updated_at
* // 関係を定義した場合、ここに関係を追加できます
* @property CompanyRecord $company 関係の例
*/
class UserRecord extends \flight\ActiveRecord
{
/**
* @var array $relations モデルの関係を設定します
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [];
/**
* コンストラクタ
* @param mixed $databaseConnection データベースへの接続
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
すべてのルートを表示する
登録されているすべてのFlightのルートを表示します。
php runway routes
特定のルートのみを表示したい場合、フラグを渡してルートをフィルタリングできます。
# GETルートのみを表示
php runway routes --get
# POSTルートのみを表示
php runway routes --post
# など
ランウェイのカスタマイズ
Flight向けのパッケージを作成しているか、プロジェクトに独自のカスタムコマンドを追加したい場合は、プロジェクト/パッケージ向けに src/commands/
、flight/commands/
、app/commands/
、または commands/
ディレクトリを作成してください。
コマンドを作成するには、AbstractBaseCommand
クラスを拡張し、__construct
メソッドとexecute
メソッドを最低限実装します。
<?php
declare(strict_types=1);
namespace flight\commands;
class ExampleCommand extends AbstractBaseCommand
{
/**
* コンストラクタ
*
* @param array<string,mixed> $config .runway-config.jsonからのJSON構成
*/
public function __construct(array $config)
{
parent::__construct('make:example', 'ドキュメントの例を作成', $config);
$this->argument('<funny-gif>', '面白いGIFの名前');
}
/**
* 関数を実行
*
* @return void
*/
public function execute(string $controller)
{
$io = $this->app()->io();
$io->info('例を作成します...');
// ここで何かを実行
$io->ok('例が作成されました!');
}
}
独自のカスタムコマンドをFlightアプリケーションに組み込む方法については、adhocore/php-cliドキュメントを参照してください!
Awesome-plugins/tracy_extensions
Tracy Flight パネル拡張
これは、Flight をより豊かに扱うための拡張機能のセットです。
- Flight - すべての Flight 変数を分析します。
- Database - ページで実行されたすべてのクエリを分析します(データベース接続を正しく開始した場合)。
- Request - すべての
$_SERVER
変数を分析し、グローバルペイロード($_GET
、$_POST
、$_FILES
)を調べます。 - Session - セッションが有効な場合、すべての
$_SESSION
変数を分析します。
これはパネルです
各パネルは、アプリケーションに関する非常に役立つ情報を表示します!
こちら をクリックしてコードを表示します。
インストール
composer require flightphp/tracy-extensions --dev
を実行すると、すぐに開始できます!
設定
これを開始するには、ほとんど設定は必要ありません。この拡張機能を使用する前に、Tracy デバッガーを開始する必要があります https://tracy.nette.org/en/guide:
<?php
use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;
// ブートストラップコード
require __DIR__ . '/vendor/autoload.php';
Debugger::enable();
// 環境を指定する必要がある場合、Debugger::enable(Debugger::DEVELOPMENT) を使用します
// アプリでデータベース接続を使用する場合、
// 開発環境でのみ使用する(本番環境では使用しないでください!)
// 通常のPDO接続と同じパラメータです
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// Flight フレームワークにこれを付与する場合
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// クエリを実行すると、時間、クエリ、パラメータをキャプチャします
// これでつながります
if(Debugger::$showBar === true) {
// これをfalseにしないと、Tracy が正しく表示されません
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app());
}
// 追加のコード
Flight::start();
追加の設定
セッションデータ
カスタムのセッションハンドラ(例: ghostff/session)を使用している場合、Tracy にセッションデータの配列を渡すと、自動的に出力されます。TracyExtensionLoader
コンストラクタの2番目のパラメータで session_data
キーを使って渡します。
use Ghostff\Session\Session;
// または flight\Session を使用します;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
if(Debugger::$showBar === true) {
// これをfalseにしないと、Tracy が正しく表示されません
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}
// ルートや他のもの...
Flight::start();
Latte
プロジェクトに Latte をインストールしている場合、Latte パネルを使用してテンプレートを分析できます。TracyExtensionLoader
コンストラクタの2番目のパラメータで latte
キーで Latte インスタンスを渡します。
use Latte\Engine;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('latte', Engine::class, [], function($latte) {
$latte->setTempDirectory(__DIR__ . '/temp');
// ここで Latte パネルを Tracy に追加します
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
});
if(Debugger::$showBar === true) {
// これをfalseにしないと、Tracy が正しく表示されません
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app());
}
Awesome-plugins/apm
FlightPHP APM ドキュメント
FlightPHP APM にようこそ—あなたのアプリのパーソナルなパフォーマンスコーチです! このガイドは、Application Performance Monitoring (APM) を FlightPHP とともに設定し、使用し、マスターするためのロードマップです。 遅いリクエストを追跡したり、レイテンシのチャートに熱中したりするかどうか、私たちがカバーしています。 アプリをより速くし、ユーザーをより幸せにし、デバッグセッションを簡単に行うようにしましょう!
APM の重要性
想像してみてください:あなたのアプリは忙しいレストランです。 注文にどれくらい時間がかかるかを追跡する方法がないと、キッチンがどこで遅れているのかを推測し、顧客が不機嫌になって去る理由を当てずっぽうに考えています。 APM はあなたの副料理長のようなものです—着信リクエストからデータベースクエリまで、すべてのステップを監視し、遅延を引き起こすものをフラグします。 遅いページはユーザーを失います(研究によると、サイトの読み込みに 3 秒以上かかると 53% が離脱する!)、APM はそれらの問題を 事前に キャッチします。 これは積極的な安心感です—「なぜこれが壊れているの?」という瞬間を少なくし、「これがどれほどスムーズに動くか!」という勝利を増やします。
インストール
Composer で始めましょう:
composer require flightphp/apm
必要なもの:
- PHP 7.4+:LTS Linux ディストリビューションとの互換性を保ちつつ、現代の PHP をサポートします。
- FlightPHP Core v3.15+:ブーストする軽量フレームワークです。
始め方
APM の素晴らしさをステップバイステップで紹介します:
1. APM を登録する
トラッキングを開始するために、index.php
または services.php
ファイルにこれを追加します:
use flight\apm\logger\LoggerFactory;
use flight\Apm;
$ApmLogger = LoggerFactory::create(__DIR__ . '/../../.runway-config.json');
$Apm = new Apm($ApmLogger);
$Apm->bindEventsToFlightInstance($app);
何が起こっているのか?
LoggerFactory::create()
はあなたの設定を入手し(すぐに詳しく説明します)、ロガーを設定します—デフォルトで SQLite です。Apm
はスターで、Flight のイベント(リクエスト、ルート、エラーなど)に耳を傾け、メトリクスを収集します。bindEventsToFlightInstance($app)
はすべてを Flight アプリに結びつけます。
プロチップ: サンプリング アプリが忙しい場合、すべての リクエストをログするとオーバーロードする可能性があります。 サンプル率(0.0 から 1.0)を使用します:
$Apm = new Apm($ApmLogger, 0.1); // リクエストの 10% をログ
これでパフォーマンスを維持しつつ、しっかりしたデータが得られます。
2. 設定する
.runway-config.json
を作成するためにこれを実行します:
php vendor/bin/runway apm:init
これは何をするのか?
- ウィザードを起動し、生のメトリクスのソースと処理されたデータの宛先を尋ねます。
- デフォルトは SQLite—例:
sqlite:/tmp/apm_metrics.sqlite
をソースに、もう一つを宛先に。 - 結果として次のような設定が得られます:
{ "apm": { "source_type": "sqlite", "source_db_dsn": "sqlite:/tmp/apm_metrics.sqlite", "storage_type": "sqlite", "dest_db_dsn": "sqlite:/tmp/apm_metrics_processed.sqlite" } }
このプロセスは、このセットアップのマイグレーションを実行するかどうかも尋ねます。 初めて設定する場合、答えは yes です。
なぜ二つの場所が必要なのか? 生のメトリクスは急速に蓄積します(フィルタリングされていないログを考えてください)。 ワーカーがそれを構造化された宛先で処理し、ダッシュボード用にします。 整理された状態を保ちます!
3. ワーカーでメトリクスを処理する
ワーカーは生のメトリクスをダッシュボード対応のデータに変換します。 一度実行します:
php vendor/bin/runway apm:worker
これは何をしているのか?
- ソース(例:
apm_metrics.sqlite
)から読み込みます。 - デフォルトのバッチサイズで最大 100 件のメトリクスを宛先で処理します。
- 完了するか、メトリクスがなくなると停止します。
継続的に実行する ライブアプリの場合、継続的な処理が必要です。 オプションは次の通りです:
-
デーモンモード:
php vendor/bin/runway apm:worker --daemon
ずっと実行し、メトリクスが来たら処理します。 開発や小規模セットアップに最適です。
-
Crontab: Crontab に追加します(
crontab -e
):* * * * * php /path/to/project/vendor/bin/runway apm:worker
毎分実行—プロダクションに最適です。
-
Tmux/Screen: 分離可能なセッションを起動:
tmux new -s apm-worker php vendor/bin/runway apm:worker --daemon # Ctrl+B, then D で分離; `tmux attach -t apm-worker` で再接続
ログアウトしても実行を続けます。
-
カスタム調整:
php vendor/bin/runway apm:worker --batch_size 50 --max_messages 1000 --timeout 300
--batch_size 50
: 一度に 50 件のメトリクスを処理。--max_messages 1000
: 1000 件のメトリクス後に停止。--timeout 300
: 5 分後に終了。
なぜこれが必要なのか? ワーカーなしではダッシュボードは空です。 これは生のログと実用的な洞察の橋渡しです。
4. ダッシュボードを起動する
アプリのバイタルサインを表示:
php vendor/bin/runway apm:dashboard
これは何?
http://localhost:8001/apm/dashboard
で PHP サーバーを起動。- リクエストログ、遅いルート、エラー率などを表示。
カスタマイズ:
php vendor/bin/runway apm:dashboard --host 0.0.0.0 --port 8080 --php-path=/usr/local/bin/php
--host 0.0.0.0
: 任意の IP からアクセス可能(リモート閲覧に便利)。--port 8080
: 8001 が使用中なら別のポートを使用。--php-path
: PATH にない場合、PHP を指定。
ブラウザで URL を開いて探検しましょう!
プロダクションモード
プロダクションでは、ファイアウォールや他のセキュリティ対策があるため、ダッシュボードを実行するためにいくつかの手法を試す必要があるかもしれません。 オプションは次の通りです:
- リバースプロキシの使用: Nginx または Apache を設定してリクエストをダッシュボードに転送。
- SSH トンネル: サーバーに SSH でアクセスできる場合、
ssh -L 8080:localhost:8001 youruser@yourserver
を使用してダッシュボードをローカルマシンにトンネル。 - VPN: サーバーが VPN の背後にあり、接続して直接ダッシュボードにアクセス。
- ファイアウォールの設定: ポート 8001 をあなたの IP またはサーバーのネットワークで開く(または設定したポート)。
- Apache/Nginx の設定: アプリケーションの前にウェブサーバーがある場合、ドメインまたはサブドメインに設定。 これを行う場合、文書ルートを
/path/to/your/project/vendor/flightphp/apm/dashboard
に設定。
違うダッシュボードが欲しい?
独自のダッシュボードを作成できます! データの表示方法のアイデアのために vendor/flightphp/apm/src/apm/presenter
ディレクトリを見てください!
ダッシュボードの機能
ダッシュボードはあなたの APM 本部です—ここに表示されるものを紹介します:
- リクエストログ: タイムスタンプ、URL、レスポンスコード、合計時間を持つすべてのリクエスト。 「詳細」をクリックしてミドルウェア、クエリ、エラーを表示。
- 最も遅いリクエスト: 時間を消費するトップ 5 のリクエスト(例: 「/api/heavy」 at 2.5s)。
- 最も遅いルート: 平均時間によるトップ 5 のルート—パターンの特定に最適。
- エラー率: 失敗するリクエストの割合(例: 2.3% 500s)。
- レイテンシパーセンタイル: 95th (p95) と 99th (p99) レスポンス時間—最悪ケースを知る。
- レスポンスコードチャート: 時間経過による 200s、404s、500s の視覚化。
- 長いクエリ/ミドルウェア: トップ 5 の遅いデータベース呼び出しとミドルウェア層。
- キャッシュヒット/ミス: キャッシュがどれほど活躍するかを示す。
追加機能:
- 「直近 1 時間」「直近 1 日」「直近 1 週間」でフィルタ。
- 深夜セッションのためにダークモードを切り替え。
例:
/users
へのリクエストは次のように表示される可能性があります:
- 合計時間: 150ms
- ミドルウェア:
AuthMiddleware->handle
(50ms) - クエリ:
SELECT * FROM users
(80ms) - キャッシュ:
user_list
のヒット (5ms)
カスタムイベントの追加
API 呼び出しや支払いプロセスなどのものを追跡:
use flight\apm\CustomEvent;
$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('api_call', [
'endpoint' => 'https://api.example.com/users',
'response_time' => 0.25,
'status' => 200
]));
どこに表示されるのか? ダッシュボードのリクエスト詳細の下の「カスタムイベント」—展開可能できれいな JSON フォーマット。
使用例:
$start = microtime(true);
$apiResponse = file_get_contents('https://api.example.com/data');
$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('external_api', [
'url' => 'https://api.example.com/data',
'time' => microtime(true) - $start,
'success' => $apiResponse !== false
]));
これでその API がアプリを遅らせているかどうかがわかります!
データベース監視
PDO クエリをこのように追跡:
use flight\database\PdoWrapper;
$pdo = new PdoWrapper('sqlite:/path/to/db.sqlite');
$Apm->addPdoConnection($pdo);
得られるもの:
- クエリテキスト(例:
SELECT * FROM users WHERE id = ?
) - 実行時間(例: 0.015s)
- 行数(例: 42)
注意点:
- オプション: DB 追跡が必要ない場合はスキップ。
- PdoWrapper のみ: コア PDO はまだフックされていません—待機中!
- パフォーマンス警告: DB が重いサイトですべてのクエリをログすると遅くなる可能性があります。 サンプリング(
$Apm = new Apm($ApmLogger, 0.1)
)を使用して負荷を軽減。
例の出力:
- クエリ:
SELECT name FROM products WHERE price > 100
- 時間: 0.023s
- 行: 15
ワーカーのオプション
ワーカーを好みに合わせて調整:
--timeout 300
: 5 分後に停止—テストに良い。--max_messages 500
: 500 件のメトリクスで上限。--batch_size 200
: 一度に 200 件処理—速度とメモリのバランス。--daemon
: 止まらず実行—ライブ監視に理想的。
例:
php vendor/bin/runway apm:worker --daemon --batch_size 100 --timeout 3600
1 時間実行、一度に 100 件のメトリクスを処理。
アプリ内のリクエスト ID
各リクエストには一意のリクエスト ID があり、追跡に使用できます。 ログとメトリクスを関連付けるために、エラーページにリクエスト ID を追加できます:
Flight::map('error', function($message) {
// Get the request ID from the response header X-Flight-Request-Id
$requestId = Flight::response()->getHeader('X-Flight-Request-Id');
// Additionally you could fetch it from the Flight variable
// This method won't work well in swoole or other async platforms.
// $requestId = Flight::get('apm.request_id');
echo "Error: $message (Request ID: $requestId)";
});
アップグレード
APM の新しいバージョンにアップグレードする場合、データベースのマイグレーションが必要になる可能性があります。 次のコマンドを実行して:
php vendor/bin/runway apm:migrate
これはデータベーススキーマを最新バージョンに更新するための必要なマイグレーションを実行します。
注意: APM データベースが大きい場合、これらのマイグレーションには時間がかかる可能性があります。 オフピーク時に実行することを検討してください。
古いデータの消去
データベースを整理するために、古いデータを消去できます。 これは忙しいアプリを実行していて、データベースのサイズを管理したい場合に特に便利です。 次のコマンドを実行して:
php vendor/bin/runway apm:purge
これはデータベースから 30 日より古いすべてのデータを削除します。 --days
オプションで日数を調整できます:
php vendor/bin/runway apm:purge --days 7
これはデータベースから 7 日より古いすべてのデータを削除します。
トラブルシューティング
困った? これを試してください:
-
ダッシュボードにデータがない?
- ワーカーが実行中ですか?
ps aux | grep apm:worker
で確認。 - 設定パスが一致しますか?
.runway-config.json
の DSN が実際のファイルを示しているか確認。 php vendor/bin/runway apm:worker
を手動で実行して保留中のメトリクスを処理。
- ワーカーが実行中ですか?
-
ワーカーエラー?
- SQLite ファイルを覗いてみてください(例:
sqlite3 /tmp/apm_metrics.sqlite "SELECT * FROM apm_metrics_log LIMIT 5"
)。 - PHP ログでスタックトレースを確認。
- SQLite ファイルを覗いてみてください(例:
-
ダッシュボードが起動しない?
- ポート 8001 が使用中ですか?
--port 8080
を使用。 - PHP が見つからない?
--php-path /usr/bin/php
を使用。 - ファイアウォールがブロック? ポートを開くか
--host localhost
を使用。
- ポート 8001 が使用中ですか?
-
遅すぎる?
- サンプル率を下げる:
$Apm = new Apm($ApmLogger, 0.05)
(5%)。 - バッチサイズを減らす:
--batch_size 20
。
- サンプル率を下げる:
Awesome-plugins/tracy
Tracy
Tracy は Flight と一緒に使用できる素晴らしいエラーハンドラです。アプリケーションのデバッグに役立つ数々のパネルがあります。拡張して独自のパネルを追加するのも非常に簡単です。Flight チームは、flightphp/tracy-extensions プラグイン用にいくつかのパネルを作成しました。
インストール
Composer でインストールします。Tracy は本番用のエラーハンドリングコンポーネントが付属しているため、実際には dev バージョンなしでインストールする必要があります。
composer require tracy/tracy
基本設定
開始するための基本的な設定オプションがあります。詳細については、Tracy ドキュメント を参照してください。
require 'vendor/autoload.php';
use Tracy\Debugger;
// Tracy を有効にする
Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) // 明示する必要がある場合もあります(Debugger::PRODUCTION も同様)
// Debugger::enable('23.75.345.200'); // IP アドレスの配列を提供することもできます
// ここにエラーと例外が記録されます。このディレクトリが存在し、書き込み可能であることを確認してください。
Debugger::$logDirectory = __DIR__ . '/../log/';
Debugger::$strictMode = true; // すべてのエラーを表示
// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // ディプリケートされた通知を除くすべてのエラー
if (Debugger::$showBar) {
$app->set('flight.content_length', false); // Debugger バーが表示されている場合、Flight によって content-length が設定できません。
// これは Flight 用の Tracy 拡張機能に固有のものです。これを含めた場合は有効にしてください。
new TracyExtensionLoader($app);
}
便利なヒント
コードのデバッグ中に、データを出力するための非常に役立つ関数がいくつかあります。
bdump($var)
- これにより、変数が Tracy バーにダンプされます(別のパネルで表示されます)。dumpe($var)
- これにより、変数がダンプされ、その後すぐにプログラムが停止します。
Awesome-plugins/active_record
Flight アクティブレコード
アクティブレコードとは、データベースのエンティティをPHPオブジェクトにマッピングすることです。簡単に言えば、データベースにユーザーテーブルがある場合、そのテーブルの行をUser
クラスと$user
オブジェクトに「翻訳」することができます。 基本例を参照してください。
GitHubのリポジトリについてはこちらをクリックしてください。
基本例
次のテーブルがあると仮定しましょう:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
password TEXT
);
このテーブルを表す新しいクラスを設定できます:
/**
* アクティブレコードクラスは通常単数です
*
* ここにテーブルのプロパティをコメントとして追加することを強くお勧めします
*
* @property int $id
* @property string $name
* @property string $password
*/
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
// このように設定できます
parent::__construct($database_connection, 'users');
// またはこのように
parent::__construct($database_connection, null, [ 'table' => 'users']);
}
}
さあ、魔法が起こるのを見てみましょう!
// sqliteの場合
$database_connection = new PDO('sqlite:test.db'); // これは単なる例ですので、実際のデータベース接続を使用することになります
// mysqlの場合
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');
// またはmysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// または非オブジェクト型のmysqli作成
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');
$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('some cool password');
$user->insert();
// または $user->save();
echo $user->id; // 1
$user->name = 'Joseph Mamma';
$user->password = password_hash('some cool password again!!!');
$user->insert();
// ここで $user->save() を使用することはできません。更新として考えられてしまいます!
echo $user->id; // 2
新しいユーザーを追加するのはとても簡単でした!データベースにユーザーロウがあるので、どうやって取り出すことができますか?
$user->find(1); // データベースで id = 1 を探して返します。
echo $user->name; // 'Bobby Tables'
すべてのユーザーを見つけたい場合はどうしますか?
$users = $user->findAll();
特定の条件で検索する場合はどうですか?
$users = $user->like('name', '%mamma%')->findAll();
どうですか?楽しいでしょう?インストールして始めましょう!
インストール
Composerで簡単にインストールできます
composer require flightphp/active-record
使用法
これはスタンドアロンライブラリとして使用することも、Flight PHPフレームワークと一緒に使用することもできます。完全にあなたの好みです。
スタンドアロン
コンストラクタにPDO接続を渡すことを確認してください。
$pdo_connection = new PDO('sqlite:test.db'); // これは単なる例で、実際にはデータベース接続を使用することになります
$User = new User($pdo_connection);
常にコンストラクタでデータベース接続を設定したくないですか?他のアイデアについてはデータベース接続管理を参照してください!
Flightでメソッドとして登録
Flight PHPフレームワークを使用している場合、ActiveRecordクラスをサービスとして登録できますが、本当にそうする必要はありません。
Flight::register('user', 'User', [ $pdo_connection ]);
// 次に、コントローラや関数などでこのように使用できます。
Flight::user()->find(1);
runway
メソッド
runway は、Flight用のCLIツールで、このライブラリ用のカスタムコマンドがあります。
# 使用法
php runway make:record database_table_name [class_name]
# 例
php runway make:record users
これにより、app/records/
ディレクトリにUserRecord.php
という新しいクラスが作成され、次の内容が含まれます:
<?php
declare(strict_types=1);
namespace app\records;
/**
* ユーザーテーブル用のアクティブレコードクラスです。
* @link https://docs.flightphp.com/awesome-plugins/active-record
*
* @property int $id
* @property string $username
* @property string $email
* @property string $password_hash
* @property string $created_dt
*/
class UserRecord extends \flight\ActiveRecord
{
/**
* @var array $relations モデルのリレーションシップを設定します
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [
// 'relation_name' => [ self::HAS_MANY, 'RelatedClass', 'foreign_key' ],
];
/**
* コンストラクタ
* @param mixed $databaseConnection データベースへの接続
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
CRUD関数
find($id = null) : boolean|ActiveRecord
1つのレコードを見つけて、現在のオブジェクトに割り当てます。何らかの$id
を渡すと、その値でプライマリキーを検索します。何も渡さない場合は、テーブル内の最初のレコードを見つけます。
他のヘルパーメソッドを渡してテーブルをクエリすることもできます。
// あらかじめ条件を付けてレコードを検索
$user->notNull('password')->orderBy('id DESC')->find();
// 特定のidでレコードを検索
$id = 123;
$user->find($id);
findAll(): array<int,ActiveRecord>
指定したテーブル内のすべてのレコードを見つけます。
$user->findAll();
isHydrated(): boolean
(v0.4.0)
現在のレコードが水和(データベースから取得)されている場合はtrue
を返します。
$user->find(1);
// データが見つかった場合...
$user->isHydrated(); // true
insert(): boolean|ActiveRecord
現在のレコードをデータベースに挿入します。
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
テキストベースのプライマリキー
テキストベースのプライマリキー(例えばUUID)がある場合、挿入前に次の2つの方法でプライマリキーの値を設定できます。
$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'some-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // または $user->save();
または、イベントを通じてプライマリキーを自動的に生成させることもできます。
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
// 上記の配列の代わりにこのようにプライマリキーを設定することもできます。
$this->primaryKey = 'uuid';
}
protected function beforeInsert(self $self) {
$self->uuid = uniqid(); // またはユニークIDを生成する必要がある他の方法
}
}
挿入前にプライマリキーを設定しないと、rowid
に設定され、データベースが自動生成しますが、そのフィールドがテーブルに存在しない場合、持続性がなくなります。したがって、これを定期的に処理するためにイベントを使うことをお勧めします。
update(): boolean|ActiveRecord
現在のレコードをデータベースに更新します。
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@example.com';
$user->update();
save(): boolean|ActiveRecord
現在のレコードをデータベースに挿入または更新します。レコードにIDがある場合は更新し、そうでない場合は挿入します。
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->save();
注: クラスでリレーションシップが定義されている場合、それらの関係も再帰的に保存されます(v0.4.0以降)。
delete(): boolean
現在のレコードをデータベースから削除します。
$user->gt('id', 0)->orderBy('id desc')->find();
$user->delete();
事前に検索を実行して複数のレコードを削除することもできます。
$user->like('name', 'Bob%')->delete();
dirty(array $dirty = []): ActiveRecord
ダーティデータとは、レコード内で変更されたデータを指します。
$user->greaterThan('id', 0)->orderBy('id desc')->find();
// この時点では何も「ダーティ」ではありません。
$user->email = 'test@example.com'; // これは変更されているので「ダーティ」と見なされます。
$user->update();
// 更新され、データベースに永続化されたので、ダーティデータはありません。
$user->password = password_hash('newpassword'); // これはダーティです。
$user->dirty(); // 引数を何も渡さないと、すべてのダーティエントリがクリアされます。
$user->update(); // 何も捕捉されていないため、何も更新されません。
$user->dirty([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // 名前とパスワードの両方が更新されます。
copyFrom(array $data): ActiveRecord
(v0.4.0)
これはdirty()
メソッドの別名です。何をしているのかがより明確です。
$user->copyFrom([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // 名前とパスワードの両方が更新されます。
isDirty(): boolean
(v0.4.0)
現在のレコードが変更された場合、true
を返します。
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@email.com';
$user->isDirty(); // true
reset(bool $include_query_data = true): ActiveRecord
現在のレコードを初期状態にリセットします。これはループ型の動作で使用するのに非常に便利です。
true
を渡すと、現在のオブジェクトを見つけるために使用されたクエリデータもリセットされます(デフォルトの動作)。
$users = $user->greaterThan('id', 0)->orderBy('id desc')->find();
$user_company = new UserCompany($pdo_connection);
foreach($users as $user) {
$user_company->reset(); // クリーンスレートで開始します
$user_company->user_id = $user->id;
$user_company->company_id = $some_company_id;
$user_company->insert();
}
getBuiltSql(): string
(v0.4.1)
find()
, findAll()
, insert()
, update()
, またはsave()
メソッドを実行した後、生成されたSQLを取得してデバッグ目的で使用できます。
SQLクエリメソッド
select(string $field1 [, string $field2 ... ])
必要に応じてテーブル内のいくつかのカラムだけを選択できます(非常に広いテーブルではパフォーマンスが向上します)
$user->select('id', 'name')->find();
from(string $table)
実際には別のテーブルも選択できます!なぜそれをしないのですか?
$user->select('id', 'name')->from('user')->find();
join(string $table_name, string $join_condition)
データベース内の別のテーブルを結合することもできます。
$user->join('contacts', 'contacts.user_id = users.id')->find();
where(string $where_conditions)
カスタムのwhere引数を設定できます(このwhere文にパラメータを設定することはできません)
$user->where('id=1 AND name="demo"')->find();
セキュリティノート - 何かこうしたくなるかもしれません $user->where("id = '{$id}' AND name = '{$name}'")->find();
。絶対にこれを実行しないでください!これはSQLインジェクション攻撃の対象です。この件に関する多くのオンライン記事がありますので、"sql injection attacks php"とGoogle検索すれば、多くの記事が見つかります。このライブラリを使用する際は、where()
メソッドの代わりに、$user->eq('id', $id)->eq('name', $name)->find();
のようにするのが正しい方法です。絶対にこのようにする必要がある場合、PDO
ライブラリには$pdo->quote($var)
があり、これがあなたのためにエスケープします。 quote()
を使用した後でなければ、where()
文で使用することはできません。
group(string $group_by_statement)/groupBy(string $group_by_statement)
特定の条件で結果をグループ化します。
$user->select('COUNT(*) as count')->groupBy('name')->findAll();
order(string $order_by_statement)/orderBy(string $order_by_statement)
返されるクエリを特定の方法でソートします。
$user->orderBy('name DESC')->find();
limit(string $limit)/limit(int $offset, int $limit)
返されるレコードの数を制限します。二つ目のintが指定された場合、SQLと同様にオフセットを制限します。
$user->orderby('name DESC')->limit(0, 10)->findAll();
WHERE条件
equal(string $field, mixed $value) / eq(string $field, mixed $value)
field = $value
の場合
$user->eq('id', 1)->find();
notEqual(string $field, mixed $value) / ne(string $field, mixed $value)
field <> $value
の場合
$user->ne('id', 1)->find();
isNull(string $field)
field IS NULL
の場合
$user->isNull('id')->find();
isNotNull(string $field) / notNull(string $field)
field IS NOT NULL
の場合
$user->isNotNull('id')->find();
greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)
field > $value
の場合
$user->gt('id', 1)->find();
lessThan(string $field, mixed $value) / lt(string $field, mixed $value)
field < $value
の場合
$user->lt('id', 1)->find();
greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)
field >= $value
の場合
$user->ge('id', 1)->find();
lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)
field <= $value
の場合
$user->le('id', 1)->find();
like(string $field, mixed $value) / notLike(string $field, mixed $value)
field LIKE $value
または field NOT LIKE $value
の場合
$user->like('name', 'de')->find();
in(string $field, array $values) / notIn(string $field, array $values)
field IN($value)
または field NOT IN($value)
の場合
$user->in('id', [1, 2])->find();
between(string $field, array $values)
field BETWEEN $value AND $value1
の場合
$user->between('id', [1, 2])->find();
OR条件
条件をOR文でラップすることも可能です。これは、startWrap()
およびendWrap()
メソッドを使用するか、フィールドと値の後に条件の3番目のパラメータを指定することで行います。
// メソッド 1
$user->eq('id', 1)->startWrap()->eq('name', 'demo')->or()->eq('name', 'test')->endWrap('OR')->find();
// これは `id = 1 AND (name = 'demo' OR name = 'test')` と評価されます
// メソッド 2
$user->eq('id', 1)->eq('name', 'demo', 'OR')->find();
// これは `id = 1 OR name = 'demo'` と評価されます
リレーションシップ
このライブラリを使用して、いくつかの種類のリレーションシップを設定できます。一対多および一対一のリレーションシップをテーブル間に設定できます。これには、事前にクラス内で追加のセットアップが必要です。
$relations
配列の設定は難しくはありませんが、正しい構文を推測することは混乱を招くことがあります。
protected array $relations = [
// キーの名前は好きなように設定できます。アクティブレコードの名前はおそらく良いでしょう。例:user、contact、client
'user' => [
// 必須
// self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
self::HAS_ONE, // これはリレーションのタイプです
// 必須
'Some_Class', // これは参照する「他の」アクティブレコードクラスです
// 必須
// リレーションシップの種類に応じて
// self::HAS_ONE = 結合を参照する外部キー
// self::HAS_MANY = 結合を参照する外部キー
// self::BELONGS_TO = 結合を参照するローカルキー
'local_or_foreign_key',
// 他のモデルのプライマリキーにのみ結合しますので、ご注意ください。
// オプショナル
[ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // リレーションを結合する際に希望する追加条件
// $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))
// オプショナル
'back_reference_name' // これは、このリレーションシップを自身に戻して参照したい場合の名前です。例:$user->contact->user;
];
]
class User extends ActiveRecord{
protected array $relations = [
'contacts' => [ self::HAS_MANY, Contact::class, 'user_id' ],
'contact' => [ self::HAS_ONE, Contact::class, 'user_id' ],
];
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
}
class Contact extends ActiveRecord{
protected array $relations = [
'user' => [ self::BELONGS_TO, User::class, 'user_id' ],
'user_with_backref' => [ self::BELONGS_TO, User::class, 'user_id', [], 'contact' ],
];
public function __construct($database_connection)
{
parent::__construct($database_connection, 'contacts');
}
}
これでリファレンスのセットアップができたので、非常に簡単に使用できます!
$user = new User($pdo_connection);
// 最も最近のユーザーを見つける。
$user->notNull('id')->orderBy('id desc')->find();
// リレーションを使用して連絡先を取得:
foreach($user->contacts as $contact) {
echo $contact->id;
}
// または反対側に行くことができます。
$contact = new Contact();
// 1つの連絡先を見つけます
$contact->find();
// リレーションを使用してユーザーを取得:
echo $contact->user->name; // これはユーザー名です
すごいですね!
カスタムデータの設定
場合によっては、アクティブレコードにカスタム計算など、オブジェクトに直接接続したいユニークなものを添付する必要があるかもしれません。
setCustomData(string $field, mixed $value)
カスタムデータは、setCustomData()
メソッドを使用して添付します。
$user->setCustomData('page_view_count', $page_view_count);
そして、通常のオブジェクトプロパティのように参照します。
echo $user->page_view_count;
イベント
このライブラリのもう一つの素晴らしい機能は、イベントに関するものです。イベントは、呼び出す特定のメソッドに基づいて特定のタイミングでトリガーされます。自動的にデータをセットアップするのに非常に役立ちます。
onConstruct(ActiveRecord $ActiveRecord, array &config)
これは、デフォルトの接続などを設定するのに非常に便利です。
// index.phpまたはbootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
//
//
//
// User.php
class User extends flight\ActiveRecord {
protected function onConstruct(self $self, array &$config) { // &参照を忘れずに
// このように接続を自動的に設定できます
$config['connection'] = Flight::db();
// またはこれ
$self->transformAndPersistConnection(Flight::db());
// この方法でテーブル名を設定することもできます。
$config['table'] = 'users';
}
}
beforeFind(ActiveRecord $ActiveRecord)
おそらく、毎回クエリ操作が必要な場合に役立ちます。
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFind(self $self) {
// 常に id >= 0 を実行します。
$self->gte('id', 0);
}
}
afterFind(ActiveRecord $ActiveRecord)
おそらく、レコードが取得されるたびに何らかのロジックを実行する必要がある場合に役立ちます。何かを復号化する必要がありますか?毎回カスタムカウントクエリを実行する必要がありますか(パフォーマンスは良くありませんが、いかがでしょうか)?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFind(self $self) {
// 何かを復号化する
$self->secret = yourDecryptFunction($self->secret, $some_key);
// 何かカスタムのストレージを行うかもしれません
$self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count']);
}
}
beforeFindAll(ActiveRecord $ActiveRecord)
おそらく、毎回クエリ操作が必要な場合に役立ちます。
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFindAll(self $self) {
// 常に id >= 0 を実行します
$self->gte('id', 0);
}
}
afterFindAll(array<int,ActiveRecord> $results)
afterFind()
に似ていますが、すべてのレコードに対して行えます!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFindAll(array $results) {
foreach($results as $self) {
// afterFind()のような何かを行います
}
}
}
beforeInsert(ActiveRecord $ActiveRecord)
毎回いくつかのデフォルト値を設定するのに非常に便利です。
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// 有効なデフォルトを設定します
if(!$self->created_date) {
$self->created_date = gmdate('Y-m-d');
}
if(!$self->password) {
$self->password = password_hash((string) microtime(true));
}
}
}
afterInsert(ActiveRecord $ActiveRecord)
挿入後にデータを変更する必要があるユースケースがあるかもしれません。
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// 自分の好きなように
Flight::cache()->set('most_recent_insert_id', $self->id);
// または何か...
}
}
beforeUpdate(ActiveRecord $ActiveRecord)
毎回更新時にデフォルト値を設定するのに非常に便利です。
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeUpdate(self $self) {
// 有効なデフォルトを設定します
if(!$self->updated_date) {
$self->updated_date = gmdate('Y-m-d');
}
}
}
afterUpdate(ActiveRecord $ActiveRecord)
更新後にデータを変更する必要があるユースケースがあるかもしれません。
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterUpdate(self $self) {
// 自分の好きなように
Flight::cache()->set('most_recently_updated_user_id', $self->id);
// または何か...
}
}
beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)
これは、挿入または更新が行われるときに、イベントを発生させたい場合に役立ちます。長い説明は省きますが、何を意味するのかを推測できますよね。
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeSave(self $self) {
$self->last_updated = gmdate('Y-m-d H:i:s');
}
}
beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)
ここで何をするかは不明ですが、判断はありません!あなたの好きなようにやってください!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeDelete(self $self) {
echo '彼は勇敢な兵士でした... :cry-face:';
}
}
データベース接続管理
このライブラリを使用する際、データベース接続をいくつかの異なる方法で設定できます。コンストラクタ内で接続を設定することも、$config['connection']
変数を介して設定することも、setDatabaseConnection()
を使用して設定することもできます(v0.4.1)。
$pdo_connection = new PDO('sqlite:test.db'); // 例として
$user = new User($pdo_connection);
// または
$user = new User(null, [ 'connection' => $pdo_connection ]);
// または
$user = new User();
$user->setDatabaseConnection($pdo_connection);
アクティブレコードを呼び出すたびに、$database_connection
を設定するのを避けたい場合は、その方法がいくつかあります!
// index.phpまたはbootstrap.php
// Flightで登録済みのクラスとしてこれを設定します
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
// User.php
class User extends flight\ActiveRecord {
public function __construct(array $config = [])
{
$database_connection = $config['connection'] ?? Flight::db();
parent::__construct($database_connection, 'users', $config);
}
}
// これで、引数は不要です!
$user = new User();
注: 単体テストを計画している場合、この方法で行うと単体テストにいくつかの課題が生じる可能性がありますが、全体的には
setDatabaseConnection()
や$config['connection']
で接続を注入できるため、あまり問題ではありません。
例えば、長時間実行されるCLIスクリプトを実行している場合に接続をリフレッシュする必要がある場合、$your_record->setDatabaseConnection($pdo_connection)
で接続を再設定することができます。
貢献
ぜひご協力ください。 :D
セットアップ
貢献する際は、composer test-coverage
を実行して100%のテストカバレッジを維持してください(これは真の単体テストカバレッジではなく、むしろ統合テストのカバレッジです)。
また、composer beautify
およびcomposer phpcs
を実行して、すべてのリンティングエラーを修正することを確認してください。
ライセンス
MIT
Awesome-plugins/latte
ラッテ
ラッテ は、非常に使いやすく、Twig や Smarty よりも PHP 構文に近いテンプレートエンジンです。フル機能を備えており、独自のフィルタや関数を追加することも非常に簡単です。
インストール
Composer を使用してインストールします。
composer require latte/latte
基本的な設定
始めるための基本的な設定オプションがあります。ラッテドキュメント で詳細を確認できます。
use Latte\Engine as LatteEngine;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('latte', LatteEngine::class, [], function(LatteEngine $latte) use ($app) {
// ここがラッテがテンプレートをキャッシュして処理を高速化する場所です
// ラッテの素晴らしい点の1つは、テンプレートを変更すると自動的にキャッシュを更新します!
$latte->setTempDirectory(__DIR__ . '/../cache/');
// ビューのルートディレクトリを示す Latte の設定
// $app->get('flight.views.path') は config.php ファイルで設定されています
// または `__DIR__ . '/../views/'` のようなものも行えます
$latte->setLoader(new \Latte\Loaders\FileLoader($app->get('flight.views.path')));
});
シンプルなレイアウトの例
以下はレイアウトファイルのシンプルな例です。このファイルは他のすべてのビューを囲むために使用されます。
<!-- app/views/layout.latte -->
<!doctype html>
<html lang="en">
<head>
<title>{$title ? $title . ' - '}My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<!-- ここにナビゲーション要素が入ります -->
</nav>
</header>
<div id="content">
<!-- これが魔法です -->
{block content}{/block}
</div>
<div id="footer">
© 著作権
</div>
</body>
</html>
そして、コンテンツブロック内でレンダリングされるファイルがあります:
<!-- app/views/home.latte -->
<!-- このファイルが layout.latte ファイル内にあることを Latte に伝えます -->
{extends layout.latte}
<!-- レイアウト内のコンテンツブロック内に表示されるコンテンツです -->
{block content}
<h1>ホームページ</h1>
<p>アプリへようこそ!</p>
{/block}
次に、この内容を関数またはコントローラ内でレンダリングする際には、次のように行います:
// シンプルなルート
Flight::route('/', function () {
Flight::latte()->render('home.latte', [
'title' => 'ホームページ'
]);
});
// もしくはコントローラを使用している場合
Flight::route('/', [HomeController::class, 'index']);
// HomeController.php
class HomeController
{
public function index()
{
Flight::latte()->render('home.latte', [
'title' => 'ホームページ'
]);
}
}
ラッテを最大限に活用するための詳細については、Latte Documentation を参照してください。
Awesome-plugins/awesome_plugins
素晴らしいプラグイン
Flight は非常に拡張性が高いです。Flight アプリケーションに機能を追加するためのいくつかのプラグインがあります。一部は Flight チームによって公式にサポートされており、他のものは Flight を始めるためのマイクロ/ライトライブラリです。
API ドキュメンテーション
API ドキュメンテーションは、どの API にとっても重要です。開発者が API とどのように相互作用するかを理解し、何を期待するかを知るのに役立ちます。Flight プロジェクトの API ドキュメンテーションを生成するためのいくつかのツールがあります。
- FlightPHP OpenAPI Generator - Daniel Schreiber によって書かれたブログ投稿で、OpenAPI 仕様を FlightPHP と一緒に使用して、API ファーストアプローチで API を構築する方法について説明しています。
- SwaggerUI - Swagger UI は、Flight プロジェクトの API ドキュメンテーションを生成するのに最適なツールです。非常に使いやすく、ニーズに合わせてカスタマイズ可能です。これは Swagger ドキュメンテーションを生成するための PHP ライブラリです。
アプリケーションのパフォーマンス監視 (APM)
アプリケーションのパフォーマンス監視 (APM) は、どのアプリケーションにとっても重要です。アプリケーションがどのように動作しているかを理解し、ボトルネックがどこにあるかを知るのに役立ちます。Flight と一緒に使用できるいくつかの APM ツールがあります。
- betaflightphp/apm - Flight APM は、Flight アプリケーションを監視するためのシンプルな APM ライブラリです。アプリケーションのパフォーマンスを監視し、ボトルネックを特定するのに使用できます。
認証/認可
認証と認可は、誰が何にアクセスできるかを制御する必要があるアプリケーションにとって重要です。
- official flightphp/permissions - 公式の Flight Permissions ライブラリです。このライブラリは、アプリケーションにユーザーとアプリケーションレベルの権限を簡単に追加するためのシンプルな方法です。
キャッシング
キャッシングは、アプリケーションの速度を向上させる優れた方法です。Flight と一緒に使用できるいくつかのキャッシングライブラリがあります。
- official flightphp/cache - 軽量でシンプルなスタンドアロンの PHP インファイルキャッシングクラス
CLI
CLI アプリケーションは、アプリケーションと相互作用するための優れた方法です。コントローラーを生成したり、すべてのルートを表示したり、その他さまざまな用途に使用できます。
- official flightphp/runway - Runway は、Flight アプリケーションを管理するための CLI アプリケーションです。
クッキー
クッキーは、クライアント側に小さなデータを保存するための優れた方法です。ユーザーの好み、アプリケーションの設定などを保存するのに使用できます。
- overclokk/cookie - PHP Cookie は、クッキーを管理するためのシンプルで効果的な PHP ライブラリを提供します。
デバッグ
デバッグは、ローカル環境で開発する際に重要です。デバッグ体験を向上させるためのいくつかのプラグインがあります。
- tracy/tracy - これは Flight と一緒に使用できる完全な機能を持つエラーハンドラーです。さまざまなパネルがあり、アプリケーションのデバッグに役立ちます。また、拡張して独自のパネルを追加することも非常に簡単です。
- flightphp/tracy-extensions - Tracy エラーハンドラーと一緒に使用され、Flight プロジェクトのデバッグに特化した追加のパネルを追加します。
データベース
データベースは、ほとんどのアプリケーションの基盤です。これを使ってデータを保存し、取得します。一部はクエリを書くためのシンプルなラッパーで、一部はフル機能の ORM です。
- official flightphp/core PdoWrapper - 公式の Flight PDO Wrapper で、core の一部です。これはクエリを書くプロセスを簡素化するためのシンプルなラッパーです。ORM ではありません。
- official flightphp/active-record - 公式の Flight ActiveRecord ORM/Mapper です。データベースにデータを簡単に取得し、保存するための優れた小さなライブラリです。
- byjg/php-migration - プロジェクトのすべてのデータベース変更を追跡するためのプラグインです。
暗号化
暗号化は、機密データを保存するアプリケーションにとって重要です。データを暗号化し、復号化するのはそれほど難しくありませんが、暗号化キーを適切に保存すること は 難しい場合があります。一番重要なのは、暗号化キーを公開ディレクトリに保存したり、コードリポジトリにコミットしたりしないことです。
- defuse/php-encryption - これはデータを暗号化し、復号化するためのライブラリです。セットアップしてデータを暗号化し、復号化するのは比較的簡単です。
ジョブキュー
ジョブキューは、タスクを非同期で処理するための非常に役立つものです。これには、メールの送信、画像の処理、リアルタイムで処理する必要がないものは何でも使用できます。
- n0nag0n/simple-job-queue - Simple Job Queue は、ジョブを非同期で処理するためのライブラリです。beanstalkd、MySQL/MariaDB、SQLite、PostgreSQL と一緒に使用できます。
セッション
セッションは API にはあまり役立ちませんが、Web アプリケーションを構築する際には、状態の維持やログイン情報の管理に重要です。
- official flightphp/session - 公式の Flight Session ライブラリです。これはセッションデータを保存し、取得するためのシンプルなセッションライブラリで、PHP の組み込みセッション処理を使用します。
- Ghostff/Session - PHP Session Manager (非ブロッキング、フラッシュ、セグメント、セッション暗号化)。オプションでセッションデータを暗号化/復号化するための PHP open_ssl を使用します。
テンプレート
テンプレートは、UI を持つ Web アプリケーションの基盤です。Flight と一緒に使用できるいくつかのテンプレートエンジンがあります。
- deprecated flightphp/core View - これは core の一部で非常に基本的なテンプレートエンジンです。プロジェクトに数ページ以上ある場合、推奨されません。
- latte/latte - Latte は、使いやすく、Twig や Smarty よりも PHP 構文に近い、完全な機能を持つテンプレートエンジンです。また、フィルターや関数を拡張して追加することも非常に簡単です。
貢献
共有したいプラグインがありますか?リストに追加するためのプルリクエストを提出してください!
Media
メディア
私たちは、Flightに関するインターネット上のさまざまなメディアの種類を追跡しようとしました。Flightについてもっと学ぶために使用できるさまざまなリソースについては、以下をご覧ください。
記事とレビュー
- 定義、生成、実装:OpenAPI GeneratorとFlightPHPによるAPIファーストアプローチ by ダニエル・シュライバー (2025)
- 2024年のベストPHPマイクロフレームワーク by n0nag0n (2024)
- Flightフレームワークを使ったRESTful APIの作成 by n0nag0n (2024)
- Flightを使用したシンプルなブログの構築 パート2 by n0nag0n (2024)
- Flightを使用したシンプルなブログの構築 パート1 by n0nag0n (2024)
- 🚀 Flightフレームワークを使用したPHPでのシンプルなCRUD APIの構築 by soheil-khaledabadi (2024)
- Flightマイクロフレームワークを使用したPHPウェブアプリケーションの構築 by アーサー・C・コードックス (2023)
- 2024年のウェブ開発のためのベストPHPフレームワーク by ラビキラン・A・S (2023)
- 2023年の包括的ガイドにおけるトップ12のPHPフレームワーク by マーケティングkbk (2023)
- あなたが(おそらく)聞いたことがない5つのPHPフレームワーク by n0nag0n (2022)
- 2023年に考慮すべきウェブ開発者向けの12のトップPHPフレームワーク by アンナ・モヌス (2022)
- クラウドサーバー上の最高のPHPマイクロフレームワーク by シャゼブ・アフメド (2021)
- ウェブ開発のための強力な15のPHPフレームワーク by AHT Tech (2020)
- FlightPHPを使用した簡単なPHPルーティング by ルーカス・コンセイサン (2019)
- 新しいPHPフレームワーク(Flight)を試してみる by レオン (2017)
- Backbonejsで動作するFlightPHPの設定 by ティモシー・トッチ (2015)
ビデオとチュートリアル
- PHPとFlightPHPを使用してIoTデバイス用のREST APIを作成 - ESP32 API by IoT Craft Hub (2024)
- PHP Flightフレームワークのシンプルな紹介ビデオ by n0nag0n (2024)
- FlightphpでのHTTPコードのヘッダーを設定する(3つのソリューション!!) by ロエル・ヴァン・デ・パール (2024)
- PHP Flightフレームワークチュートリアル。超簡単なAPIプロジェクト! by n0nag0n (2022)
- PHPとMySQL、Bootstrapを使用したCRUDウェブアプリケーション by Devlopteca - オスカー・ウー (2021)
- DevOps&SysAdmin:Flight PHPマイクロフレームワーク用のLighttpdリライトルール by ロエル・ヴァン・デ・パール (2021)
- チュートリアルREST API Flight PHP #PART2 テーブル情報挿入 #コード(タガログ) by Info Singkat Official (2020)
- チュートリアルREST API Flight PHP #PART1 情報 #コード(タガログ) by Info Singkat Official (2020)
- PHPでJSON REST APIを作成する方法 - パート2 by Codewife (2018)
- PHPでJSON REST APIを作成する方法 - パート1 by Codewife (2018)
- テストマイクロフレームワークPHP - Flight PHP、Lumen、Slim 3、Laravel by Codemarket (2016)
- チュートリアル1 Flight PHP - インストール by absagg (2014)
- チュートリアル2 Flight PHP - ルートパート1 by absagg (2014)
Examples
迅速なスタートが必要ですか?
新しいFlightプロジェクトを始めるためのオプションは2つあります:
- フルスケルトンボイラープレート: コントローラーとビューを含む、より完全な例です。
- シングルファイルスケルトンボイラープレート: アプリをシンプルな単一ファイルで実行するために必要なすべてを含む単一ファイルです。
コミュニティが提供した例:
- flightravel: FlightPHPとLaravelディレクトリ、PHPツール + GHアクションが含まれています。
- fleact - ReactJSとの統合を持つFlightPHPスターターキット。
- flastro - Astro統合を持つFlightPHPスターターキット。
- velt - Veltは、FlightPHPバックエンドを持つ迅速で簡単なSvelteスターターテンプレートです。
インスピレーションが必要ですか?
これらはFlightチームによって公式にスポンサーされているわけではありませんが、Flightを使って構築された自分のプロジェクトを構成する方法についてのアイデアを得ることができます!
- Decay - HTMXとSleekDBを使用し、ゾンビに関するFlight v3! (デモ)
- Flight Example Blog - ミドルウェア、コントローラー、アクティブレコード、Latteを使ったFlight v3。
- Flight CRUD RESTful API - Flightフレームワークを使用したシンプルなCRUD APIプロジェクトで、新しいユーザーがCRUD操作とデータベース接続を迅速に設定できる基本ストラクチャを提供します。このプロジェクトは、RESTful API開発のためのFlightの使用法を示しており、初心者にとって理想的な学習ツールであり、より経験豊富な開発者にとって有用なスターターキットです。
- Flight School Management System - Flight v3
- コメント付きのペーストビン - Flight v3
- 基本的なスケルトンアプリ
- 例のウィキ
- ITイノベーターPHPフレームワークアプリケーション
- LittleEducationalCMS (スペイン語)
- イタリアのイエローページAPI
- 一般的なコンテンツ管理システム (非常に少ないドキュメント付き)
- Flightとmedooに基づく小さなphpフレームワーク。
- 例のMVCアプリケーション
自分の例を共有したいですか?
共有したいプロジェクトがある場合は、このリストに追加するためのプルリクエストを送信してください!
Install/install
インストール
ファイルのダウンロード
システムにPHPがインストールされていることを確認してください。されていない場合は、システムにインストールする方法についてはこちらをクリックしてください。
Composerを使用している場合は、次のコマンドを実行できます:
composer require flightphp/core
もしくはファイルをダウンロードして、それらをウェブディレクトリに直接展開します。
Webサーバーの設定
組み込みのPHP開発サーバー
これは最も簡単に立ち上げる方法です。組み込みサーバーを使用してアプリケーションを実行したり、SQLiteをデータベースとして使用したりすることができます(システムにsqlite3がインストールされている限り)、そして多くのものを必要としません! PHPがインストールされたら、次のコマンドを実行してください:
php -S localhost:8000
その後、ブラウザを開いてhttp://localhost:8000
に移動します。
プロジェクトのドキュメントルートを異なるディレクトリにする場合(例: プロジェクトが ~/myproject
であるが、ドキュメントルートは ~/myproject/public/
である場合)、~/myproject
ディレクトリにいる場合は、以下のコマンドを実行できます:
php -S localhost:8000 -t public/
その後、ブラウザを開いてhttp://localhost:8000
に移動します。
Apache
Apacheが既にシステムにインストールされていることを確認してください。されていない場合は、システムにApacheをインストールする方法をGoogleで調べてください。
Apacheの場合、.htaccess
ファイルを次のように編集してください:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
注意: サブディレクトリでflightを使用する必要がある場合は、
RewriteEngine On
の直後に次の行を追加してください:RewriteBase /subdir/
注意: dbやenvファイルなどのすべてのサーバーファイルを保護する必要がある場合は、
.htaccess
ファイルに次の内容を追加してください:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Nginxが既にシステムにインストールされていることを確認してください。Nginxをインストールしていない場合は、システムにNginx Apacheをインストールする方法をGoogleで調べてください。
Nginxの場合、サーバー宣言に次の内容を追加してください:
server {
location / {
try_files $uri $uri/ /index.php;
}
}
index.php
ファイルの作成
<?php
// Composerを使用している場合は、オートローダーを要求します。
require 'vendor/autoload.php';
// Composerを使用していない場合は、フレームワークを直接ロードしてください
// require 'flight/Flight.php';
// 次にルートを定義し、リクエストを処理するための関数を割り当てます。
Flight::route('/', function () {
echo 'hello world!';
});
// 最後に、フレームワークを起動します。
Flight::start();
PHPのインストール
すでにシステムに php
がインストールされている場合は、この手順をスキップしてダウンロードセクションに移動してください
分かりました!macOS、Windows 10/11、Ubuntu、Rocky Linux にPHPをインストールする手順についての説明があります。さらに、異なるバージョンのPHPをインストールする方法についても説明します。
Guides
ガイド
Flight PHPはシンプルでありながら強力に設計されており、私たちのガイドは、現実のアプリケーションをステップバイステップで構築するのに役立ちます。これらの実践的なチュートリアルは、Flightを効果的に使用する方法を示すために、完全なプロジェクトを通じて案内します。
公式ガイド
ブログの構築
Flight PHPを使用して機能的なブログアプリケーションを作成する方法を学びます。このガイドでは以下の内容を説明します:
- プロジェクト構造の設定
- Latteを使用したテンプレートの操作
- 投稿のためのルートの実装
- データの保存と取得
- フォームの送信の処理
- 基本的なエラーハンドリング
このチュートリアルは、すべての要素がどのようにリアルなアプリケーションに組み合わさるのかを見ることを望む初心者に最適です。
非公式ガイド
これらのガイドはFlightチームによって公式に維持されているわけではありませんが、コミュニティによって作成された貴重なリソースです。さまざまなトピックとユースケースをカバーし、Flight PHPの使用に関する追加の洞察を提供します。
Flight Frameworkを使用したRESTful APIの作成
このガイドでは、Flight PHPフレームワークを使用してRESTful APIを作成する方法を説明します。APIの設定、ルートの定義、JSONレスポンスを返す基本についてカバーします。
シンプルなブログの構築
このガイドでは、Flight PHPフレームワークを使用して基本的なブログを作成する方法を説明します。実際には2部構成で、基本についてのガイドと、プロダクションに対応したブログのためのより高度なトピックと改良についてのガイドがあります。
- Flightを使用したシンプルなブログの構築 - パート1 - シンプルなブログの始め方。
- Flightを使用したシンプルなブログの構築 - パート2 - プロダクションのためのブログの洗練。
PHPでのポケモンAPIの構築: 初心者向けガイド
この楽しいガイドでは、Flight PHPを使用してシンプルなポケモンAPIを作成する方法を説明します。APIの設定、ルートの定義、JSONレスポンスを返す基本についてカバーします。
貢献
ガイドのアイデアがありますか?間違いを見つけましたか?貢献を歓迎します!私たちのガイドは、FlightPHPドキュメントリポジトリで維持されています。
Flightを使用して興味深いものを作成し、それをガイドとして共有したい場合は、プルリクエストを提出してください。知識を共有することで、Flightコミュニティの成長を助けます。
APIドキュメントをお探しですか?
Flightのコア機能やメソッドについての具体的な情報をお探しなら、私たちのドキュメントの学習セクションをチェックしてください。