Learn
Flightについて学ぶ
Flightは、PHPのための高速でシンプルな拡張可能なフレームワークです。非常に多用途であり、あらゆる種類のウェブアプリケーションの構築に使用できます。
シンプルさを念頭に置いて構築されており、理解しやすく、使いやすい形で書かれています。
重要なフレームワークの概念
フレームワークを使う理由
なぜフレームワークを使用すべきかについての短い記事があります。フレームワークを使い始める前に、その利点を理解するのは良い考えです。
さらに、@lubianaによって素晴らしいチュートリアルが作成されました。具体的にFlightについて詳細に説明しているわけではありませんが、
このガイドは、フレームワークに関する主要な概念を理解し、なぜそれらを使用することが有益であるかを理解するのに役立ちます。
チュートリアルはこちらで見つけることができます。
Flightと他のフレームワークの比較
Laravel、Slim、Fat-Free、Symfonyなどの他のフレームワークからFlightに移行している場合、このページは両者の違いを理解するのに役立ちます。
コアトピック
オートローディング
アプリケーションで独自のクラスをオートロードする方法を学びます。
ルーティング
ウェブアプリケーションのルートを管理する方法を学びます。これには、ルートのグループ化、ルートパラメータ、およびミドルウェアも含まれます。
ミドルウェア
アプリケーションでリクエストとレスポンスをフィルタリングするためにミドルウェアを使用する方法を学びます。
リクエスト
アプリケーションでリクエストとレスポンスを処理する方法を学びます。
レスポンス
ユーザーにレスポンスを送信する方法を学びます。
イベント
アプリケーションにカスタムイベントを追加するためのイベントシステムの使用方法を学びます。
HTMLテンプレート
組み込みのビューエンジンを使用してHTMLテンプレートをレンダリングする方法を学びます。
セキュリティ
一般的なセキュリティ脅威からアプリケーションを保護する方法を学びます。
設定
アプリケーションのためにフレームワークを設定する方法を学びます。
Flightの拡張
独自のメソッドやクラスを追加してフレームワークを拡張する方法を学びます。
イベントとフィルタリング
イベントシステムを使用してメソッドや内部フレームワークメソッドにフックを追加する方法を学びます。
依存性注入コンテナ
アプリケーションの依存関係を管理するために依存性注入コンテナ(DIC)を使用する方法を学びます。
フレームワークAPI
フレームワークのコアメソッドについて学びます。
v3への移行
互換性はほとんど維持されていますが、v2からv3に移行する際に知っておくべきいくつかの変更があります。
トラブルシューティング
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/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のマッパブルメソッドの一部であり、必要に応じてその動作をオーバーライドできます。
このガイドでは、イベントの価値、使用方法、および初心者がその力を理解するのに役立つ実践的な例を含め、イベントの使用を開始するために必要なすべての情報をカバーします。
なぜイベントを使用するのか?
イベントは、アプリケーションの異なる部分を分離して、それぞれが過度に依存しないようにします。この分離は、しばしばデカップリングと呼ばれ、コードの更新、拡張、デバッグを容易にします。すべてを大きな塊で書く代わりに、特定のアクション(イベント)に応答する小さく独立したロジックに分割できます。
ブログアプリを構築していると想像してください:
- ユーザーがコメントを投稿したとき、次のことを行いたいかもしれません:
- コメントをデータベースに保存する。
- ブログの所有者にメールを送信する。
- セキュリティのためにアクションを記録する。
イベントがないと、すべてを1つの関数に無理やり詰め込むことになります。イベントを使用すると、コメントを保存する部分、'comment.posted'
のようなイベントをトリガーする部分、そしてメールやログを処理するリスナーを別々に設けることができます。これにより、コードがすっきりし、コアのロジックに触れずに機能(通知など)を追加または削除できます。
一般的な使用例
- ロギング:ログインやエラーのようなアクションを記録し、メインのコードを混乱させずに済みます。
- 通知:何かが発生したときにメールやアラートを送信します。
- 更新:キャッシュを更新したり、他のシステムに変更を通知したりします。
イベントリスナーの登録
イベントをリッスンするには、Flight::onEvent()
を使用します。このメソッドは、イベントが発生したときに何が起こるべきかを定義することを可能にします。
構文
Flight::onEvent(string $event, callable $callback): void
$event
:イベントの名前(例:'user.login'
)。$callback
:イベントがトリガーされたときに実行する関数。
仕組み
あなたは、イベントが発生したときにFlightに何をするかを知らせることで、「サブスクライブ」します。コールバックは、イベントトリガーから渡される引数を受け取ることができます。
Flightのイベントシステムは同期的であり、これは各イベントリスナーが順次実行され、1つずつ処理されることを意味します。イベントをトリガーすると、そのイベントのすべての登録されたリスナーが実行を完了するまで、あなたのコードは続行しません。これは非同期イベントシステムとは異なり、リスナーが並行して動作したり、後で実行される可能性があるため、理解することが重要です。
簡単な例
Flight::onEvent('user.login', function ($username) {
echo "お帰りなさい、$username!";
});
ここでは、'user.login'
イベントがトリガーされると、ユーザーの名前で挨拶します。
重要なポイント
- 同じイベントに複数のリスナーを追加できます。リスナーは登録した順番で実行されます。
- コールバックは関数、無名関数、またはクラスのメソッドである可能性があります。
イベントをトリガーする
イベントを発生させるには、Flight::triggerEvent()
を使用します。これにより、Flightはそのイベントのために登録されたすべてのリスナーを実行し、提供されたデータを渡します。
構文
Flight::triggerEvent(string $event, ...$args): void
$event
:トリガーしているイベントの名前(登録されたイベントと一致する必要があります)。...$args
:リスナーに送信するオプションの引数(任意の数の引数が可能です)。
簡単な例
$username = 'alice';
Flight::triggerEvent('user.login', $username);
これは、'user.login'
イベントをトリガーし、先に定義したリスナーに'alice'
を送信します。その結果、お帰りなさい、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("新しいイベントリスナーが追加されました: $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 が " . date('Y-m-d H:i:s') . " にログインしました");
});
// ルートを定義
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "ログインしました!";
});
Flight::start();
- 長所:シンプルで追加ファイルなし、小さなプロジェクトに最適。
- 短所:イベントとルートが増えると混沌とする可能性。
オプション 2: 別のevents.php
ファイル
少し大きなアプリでは、app/config/events.php
のような専用ファイルにイベント登録を移動することを検討してください。このファイルをindex.php
内のルートの前に含めます。これは、Flightプロジェクトでのルートがapp/config/routes.php
に整理されているのと類似しています。
// app/config/events.php
Flight::onEvent('user.login', function ($username) {
error_log("$username が " . date('Y-m-d H:i:s') . " にログインしました");
});
Flight::onEvent('user.registered', function ($email, $name) {
echo "$email宛にメールを送信しました: ようこそ、$nameさん!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "ログインしました!";
});
Flight::start();
- 長所:
index.php
がルーティングに集中できる、論理的にイベントを整理、見つけやすく編集しやすい。 - 短所:若干の構造的な追加があり、小さなアプリには過剰に感じることも。
オプション 3: トリガー近くに
別のアプローチは、トリガーされる場所の近くでイベントを登録することです。コントローラーやルート定義の中で行います。これは、イベントがアプリの特定の部分に特有である場合に効果的です。
Flight::route('/signup', function () {
// ここでイベントを登録
Flight::onEvent('user.registered', function ($email) {
echo "$email宛にようこそメールが送信されました!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "サインアップしました!";
});
- 長所:関連するコードを一緒に保つ、孤立した機能に良い。
- 短所:イベント登録が散在し、すべてのイベントを一度に見るのが難しくなる; 慎重でないと重複登録のリスクがあります。
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 がログインしました");
});
Flight::onEvent('user.registered', function ($email) {
echo "$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 が $time にログインしました");
});
// ステップ 2: アプリ内でトリガーする
Flight::route('/login', function () {
$username = 'bob'; // これはフォームから来たと仮定
Flight::triggerEvent('user.login', $username);
echo "こんにちは、$username!";
});
なぜ役立つのか:ログインコードはロギングのことを考える必要がなく、ただイベントをトリガーします。後で、他のリスナー(例:ウェルカムメールを送信)を追加できるのです。
例 2: 新しいユーザーについて通知
// 新しい登録のリスナー
Flight::onEvent('user.registered', function ($email, $name) {
// メールを送信するシミュレーション
echo "$email宛にメールが送信されました: ようこそ、$nameさん!";
});
// 誰かがサインアップするときにトリガー
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "サインアップありがとうございました!";
});
なぜ役立つのか:サインアップのロジックはユーザーの作成に集中し、イベントが通知を処理します。後で、他のリスナー(例:サインアップをログに記録)を追加できます。
例 3: キャッシュをクリアする
// キャッシュをクリアするリスナー
Flight::onEvent('page.updated', function ($pageId) {
unset($_SESSION['pages'][$pageId]); // 該当する場合、セッションキャッシュをクリア
echo "$pageIdのキャッシュがクリアされました。";
});
// ページが編集されるときにトリガー
Flight::route('/edit-page/(@id)', function ($pageId) {
// ページを更新したと仮定
Flight::triggerEvent('page.updated', $pageId);
echo "$pageIdページが更新されました。";
});
なぜ役立つのか:編集コードはキャッシングのことを考える必要がなく、更新を信号として発信します。他のアプリの部分が必要に応じて反応できます。
ベストプラクティス
- イベント名を明確に:
'user.login'
や'page.updated'
のように具体的な名前を使用してください。どのような役割か明確になるので。 - リスナーをシンプルに保つ:リスナー内に遅いまたは複雑なタスクを置かないように—アプリを速く保ちます。
- イベントをテストする:手動でトリガーして、リスナーが期待通りに機能することを確認してください。
- イベントを適切に使用する:デカップリングには優れていますが、多すぎるとコードが追いにくくなることがあるため、意味があるときに使用してください。
Flight::onEvent()
とFlight::triggerEvent()
によるFlight PHPのイベントシステムは、柔軟なアプリケーションを構築するためのシンプルでありながら強力な方法を提供します。アプリの異なる部分がイベントを通じてお互いに話し合うことを可能にすることにより、コードを整理し、再利用可能で、拡張しやすくできます。アクションを記録したり、通知を送信したり、更新を管理したりする際に、ロジックを絡ませることなく行うのに役立ちます。さらに、これらのメソッドをオーバーライドできることで、ニーズに合わせてシステムをカスタマイズする自由があります。最初は単一のイベントから始め、その後アプリの構造がどのように変わっていくかを見てみてください。
ビルトインイベント
Flight PHPには、フレームワークのライフサイクルにフックするために使用できるいくつかのビルトインイベントがあります。これらのイベントは、リクエスト/レスポンスサイクルの特定のポイントでトリガーされ、特定のアクションが発生したときにカスタムロジックを実行することを可能にします。
ビルトインイベント一覧
flight.request.received
:リクエストが受信され、解析され、処理されたときにトリガーされます。flight.route.middleware.before
:beforeミドルウェアが実行された後にトリガーされます。flight.route.middleware.after
:afterミドルウェアが実行された後にトリガーされます。flight.route.executed
:ルートが実行され、処理された後にトリガーされます。flight.response.sent
:レスポンスがクライアントに送信された後にトリガーされます。
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用の高速でシンプル、拡張可能なフレームワークです。非常に多用途で、あらゆる種類のウェブアプリケーションを構築するために使用できます。シンプルさを念頭に置いて構築されており、理解しやすく、使いやすい形式で記述されています。
Flightは、PHPを学び始めたばかりの初心者にとって素晴らしいフレームワークであり、ウェブアプリケーションの構築を学びたい方に最適です。また、ウェブアプリケーションに対してより多くの制御を求める経験豊富な開発者にも素晴らしいフレームワークです。RESTful API、シンプルなウェブアプリケーション、または複雑なウェブアプリケーションを簡単に構築できるように設計されています。
クイックスタート
まず、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フレームワークの一つです。すべてのベンチマークはTechEmpowerで確認できます。
以下は、他の人気のPHPフレームワークとのベンチマークです。
フレームワーク | プレーン・テキストのリクエスト/sec | JSONリクエスト/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にアクセスして、使い始める方法を確認してください!また、Flightでできることのインスピレーションを得られる例のページも訪れてみてください。
コミュニティ
私たちはMatrixチャットで活動しています。
そしてDiscordでも。
貢献
Flightに貢献する方法は2つあります:
- コアリポジトリを訪問して、コアフレームワークに貢献できます。
- ドキュメントに貢献できます。このドキュメントサイトは、Githubにホストされています。エラーに気付いたり、より良い内容を充実させたい場合は、遠慮なく修正してプルリクエストを送信してください!私たちは物事を把握しようとしていますが、更新や言語翻訳を歓迎します。
要件
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を使用します。ファイル、MySQL、Redis、Memcachedをサポートしています。
こちらをクリックしてコードを表示します。
インストール
コンポーザーでインストールします。
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();
// セッション設定ファイルへのカスタムパスを設定し、セッションIDにランダムな文字列を与えます
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
// または手動で設定オプションをオーバーライドすることもできます
$session->updateConfiguration([
// セッションデータをデータベースに保存したい場合(「すべてのデバイスからログアウトする」機能のように)
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', # PDO DNS用のデータベースドライバー(例:mysql:host=...;dbname=...)
'host' => '127.0.0.1', # データベースホスト
'db_name' => 'my_app_database', # データベース名
'db_table' => 'sessions', # データベーステーブル
'db_user' => 'root', # データベースユーザー名
'db_pass' => '', # データベースパスワード
'persistent_conn'=> false, # スクリプトがデータベースと通信するたびに新しい接続を確立するオーバーヘッドを避け、その結果、より高速なWebアプリケーションになります。自分で裏側を見つけてください
]
]);
}
);
助けて!セッションデータが永続化されていません!
セッションデータを設定しているのに、それがリクエスト間で永続化されていないですか?セッションデータをコミットするのを忘れているかもしれません。セッションデータを設定した後に$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_id' => $session->get('user_id')]);
}
});
Flight::route('/logout', function() {
$session = Flight::session();
$session->clear(); // すべてのセッションデータをクリア
Flight::json(['message' => '正常にログアウトしました']);
});
Flight::start();
重要なポイント
- ノンブロッキング: セッションスタートのデフォルトとして
read_and_close
を使用し、セッションロックの問題を防ぎます。 - 自動コミット: デフォルトで有効になっており、無効にされない限り、シャットダウン時に変更が自動的に保存されます。
- ファイルストレージ: セッションはデフォルトで
/flight_sessions
の下にあるシステムの一時ディレクトリに保存されます。
設定
セッションハンドラーを登録する際に、オプションの配列を渡すことでカスタマイズできます:
$app->register('session', Session::class, [
'save_path' => '/custom/path/to/sessions', // セッションファイルのディレクトリ
'encryption_key' => 'a-secure-32-byte-key-here', // 暗号化を有効にする(AES-256-CBCに推奨される32バイト)
'auto_commit' => false, // 手動制御のため自動コミットを無効にする
'start_session' => true, // 自動的にセッションを開始する(デフォルト: true)
'test_mode' => false // 開発用にテストモードを有効にする
]);
設定オプション
オプション | 説明 | デフォルト値 |
---|---|---|
save_path |
セッションファイルが保存されるディレクトリ | sys_get_temp_dir() . '/flight_sessions' |
encryption_key |
AES-256-CBC暗号化用のキー(オプション) | null (暗号化なし) |
auto_commit |
シャットダウン時にセッションデータを自動保存 | true |
start_session |
自動的にセッションを開始 | true |
test_mode |
PHPセッションに影響を与えずにテストモードで実行 | false |
test_session_id |
テストモード用のカスタムセッションID(オプション) | 設定されていない場合はランダムに生成 |
高度な使い方
手動コミット
自動コミットを無効にすると、変更を手動でコミットする必要があります:
$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を再生成します(例: ログイン後):
Flight::route('/post-login', function() {
$session = Flight::session();
$session->regenerate(); // 新しいID、データを保持
// または
$session->regenerate(true); // 新しいID、古いデータを削除
});
ミドルウェアの例
セッションベースの認証でルートを保護します:
Flight::route('/admin', function() {
Flight::json(['message' => '管理パネルへようこそ']);
})->addMiddleware(function() {
$session = Flight::session();
if (!$session->get('is_admin')) {
Flight::halt(403, 'アクセス拒否');
}
});
これはミドルウェアでの使い方の簡単な例です。詳細な例については、ミドルウェアのドキュメントを参照してください。
メソッド
Session
クラスは以下のメソッドを提供します:
set(string $key, $value)
: セッションに値を保存します。get(string $key, $default = null)
: 値を取得し、キーが存在しない場合のオプションのデフォルトを提供します。delete(string $key)
: セッションから特定のキーを削除します。clear()
: すべてのセッションデータを削除します。commit()
: 現在のセッションデータをファイルシステムに保存します。id()
: 現在のセッションIDを返します。regenerate(bool $deleteOld = false)
: セッションIDを再生成し、オプションで古いデータを削除します。
get()
とid()
を除くすべてのメソッドは、チェーンのためにSession
インスタンスを返します。
このプラグインを使用する理由
- 軽量: 外部依存関係なし—ただのファイル。
- ノンブロッキング: デフォルトで
read_and_close
でセッションロックを回避。 - 安全: 機密データのためのAES-256-CBC暗号化をサポート。
- 柔軟: 自動コミット、テストモードおよび手動コントロールオプション。
- Flightネイティブ: Flightフレームワークのために特別に構築されています。
技術的詳細
- ストレージ形式: セッションファイルは
sess_
でプレフィックスされ、設定されたsave_path
に保存されます。暗号化データはE
プレフィックス、平文はP
を使用します。 - 暗号化:
encryption_key
が提供される場合、各セッション書き込みに対してランダムIVを使用したAES-256-CBCを使用します。 - ガーベジコレクション: 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 Panel Extensions
これは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接続と同じパラメーターを持っています
$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
コンストラクターの第二パラメーターで session_data
キーで渡します。
use Ghostff\Session\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
コンストラクターの第二パラメーターで latte
キーでLatteインスタンスを渡すことができます。
use Latte\Engine;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('latte', Engine::class, [], function($latte) {
$latte->setTempDirectory(__DIR__ . '/temp');
// これでLatte Panelを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/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チームによって公式にサポートされているものもあり、他には始めるためのマイクロ/ライトライブラリもあります。
APIドキュメンテーション
APIドキュメンテーションは、あらゆるAPIにとって重要です。開発者がAPIとどのように対話し、何を期待できるかを理解するのに役立ちます。FlightプロジェクトのAPIドキュメンテーションを生成するためのツールがいくつか用意されています。
- FlightPHP OpenAPI Generator - FlightPHPを使用してAPIファーストアプローチでAPIを構築する方法についてDaniel Schreiberが書いたブログ投稿。
- SwaggerUI - Swagger UIは、FlightプロジェクトのAPIドキュメンテーションを生成するのに役立つ素晴らしいツールです。非常に使いやすく、ニーズに合わせてカスタマイズできます。これはSwaggerドキュメンテーションを生成するためのPHPライブラリです。
認証/認可
認証と認可は、誰が何にアクセスできるかを管理するために必要なアプリケーションにとって重要です。
- official flightphp/permissions - 公式Flight権限ライブラリ。このライブラリは、アプリケーションにユーザーとアプリケーションレベルの権限を追加するための簡単な方法です。
キャッシング
キャッシングはアプリケーションを高速化するための優れた方法です。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ラッパー。これは、クエリを書いて実行するプロセスを簡素化するためのシンプルなラッパーです。ORMではありません。
- official flightphp/active-record - 公式Flight ActiveRecord ORM/マッパー。データベース内のデータを簡単に取得して保存するための素晴らしい小さなライブラリです。
- byjg/php-migration - プロジェクトのすべてのデータベース変更を追跡するためのプラグインです。
暗号化
暗号化は、機密データを保存するアプリケーションにとって重要です。データを暗号化および復号化することはそれほど難しくありませんが、暗号化キーを適切に保存することは難しいことがあります。最も重要なのは、暗号化キーを公開ディレクトリに保存したり、コードリポジトリにコミットしたりしないことです。
- defuse/php-encryption - これはデータを暗号化および復号化するために使用できるライブラリです。データの暗号化と復号化を開始するのは比較的簡単です。
ジョブキュー
ジョブキューは、非同期にタスクを処理するのに非常に便利です。これには、メールの送信、画像の処理、リアルタイムで行う必要がないその他の作業が含まれます。
- n0nag0n/simple-job-queue - Simple Job Queueは、ジョブを非同期に処理するために使用できるライブラリです。beanstalkd、MySQL/MariaDB、SQLite、PostgreSQLで使用できます。
セッション
セッションはAPIにはあまり便利ではありませんが、Webアプリケーションを構築するためには、状態とログイン情報を維持するために重要です。
- official flightphp/session - 公式Flightセッションライブラリ。これはセッションデータを保存および取得するために使用できるシンプルなセッションライブラリです。PHPの組み込みのセッションハンドリングを使用しています。
- Ghostff/Session - PHPセッションマネージャー(非ブロッキング、フラッシュ、セグメント、セッション暗号化)。セッションデータのオプションの暗号化/復号化にPHP open_sslを使用します。
テンプレーティング
テンプレーティングは、UIを持つWebアプリケーションの中心です。Flightと一緒に使用できるテンプレーティングエンジンがいくつかあります。
- deprecated flightphp/core View - これはコアの一部である非常に基本的なテンプレーティングエンジンです。プロジェクトにページが数ページ以上ある場合は使用することをお勧めしません。
- 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のコア機能やメソッドについての具体的な情報をお探しなら、私たちのドキュメントの学習セクションをチェックしてください。