Learn

Flight について学ぶ

Flight は、速く、シンプルで、拡張可能な PHP フレームワークです。非常に多用途で、どんな種類のウェブアプリケーションでも構築できます。
シンプルさを念頭に置いて設計されており、理解しやすく使いやすい方法で書かれています。

🚀 AI & Developer Experience with Flight

Flight は、速くてシンプルなだけでなく、現代の AI ツールを使ってよりスマートにコードを書いて生産性を高めるように設計されています。AI 駆動のコーディングアシスタントを使ったり、反復的なタスクを自動化したり、生産性を向上させたい場合、Flight の軽量なフットプリントとストレートな構造は、最新の開発体験と AI ワークフローを一緒に使うのに最適です。

始め方を気になりますか? Explore our AI & DevEx guide を探して、Flight を使用して速く賢く作業するためのヒント、ツール、実世界の例を学んでください!

重要なフレームワークの概念

Why a Framework?

フレームワークを使う理由についての短い記事です。フレームワークを使う前に、その利点を理解することが良い考えです。

また、@lubiana によって作成された素晴らしいチュートリアルがあります。Flight について詳しく説明していませんが、
このガイドはフレームワークの主要な概念とその利点を理解するのに役立ちます。
チュートリアルは here で見つかります。

Flight Compared to Other Frameworks

Laravel、Slim、Fat-Free、または Symfony などの他のフレームワークから Flight に移行する場合、このページは両者の違いを理解するのに役立ちます。

コアトピック

AI & Developer Experience

AI ツールと現代の開発ワークフローで Flight がどのように連携して、速くスマートにコードを書くかを学んでください。

Autoloading

アプリケーションで独自のクラスを自動ロードする方法を学んでください。

Routing

ウェブアプリケーションのルートを管理する方法を学んでください。これにはルートのグループ化、ルートパラメータ、およびミドルウェアが含まれます。

Middleware

アプリケーションでリクエストとレスポンスをフィルタリングするためにミドルウェアを使う方法を学んでください。

Requests

アプリケーションでリクエストとレスポンスを扱う方法を学んでください。

Responses

ユーザーにレスポンスを送信する方法を学んでください。

Events

アプリケーションにカスタムイベントを追加するためにイベントシステムを使う方法を学んでください。

HTML Templates

組み込みのビューエンジンを使って HTML テンプレートをレンダリングする方法を学んでください。

Security

一般的なセキュリティ脅威からアプリケーションを保護する方法を学んでください。

Configuration

アプリケーションのためにフレームワークを設定する方法を学んでください。

Extending Flight

独自のメソッドとクラスを追加してフレームワークを拡張する方法を学んでください。

Events and Filtering

イベントシステムを使ってメソッドや内部フレームワークメソッドにフックを追加する方法を学んでください。

Dependency Injection Container

アプリケーションの依存関係を管理するために依存性注入コンテナ (DIC) を使う方法を学んでください。

Framework API

フレームワークのコアメソッドについて学んでください。

Migrating to v3

後方互換性はほとんど維持されていますが、v2 から v3 に移行する際に知っておくべきいくつかの変更点があります。

Troubleshooting

Flight を使用する際に遭遇する可能性のある一般的な問題をトラブルシューティングするのに役立つページです。

Learn/stopping

停止

フレームワークをhaltメソッドを呼び出すことでいつでも停止できます:

Flight::halt();

オプションでHTTPステータスコードとメッセージを指定することもできます:

Flight::halt(200, 'Be right back...');

haltを呼び出すと、その時点までのレスポンスコンテンツが破棄されます。フレームワークを停止して現在のレスポンスを出力するには、stopメソッドを使用してください:

Flight::stop();

Learn/errorhandling

エラー処理

エラーと例外

Flight によってすべてのエラーや例外がキャッチされ、error メソッドに渡されます。 デフォルトの動作は、いくつかのエラー情報を含む汎用 HTTP 500 内部サーバーエラー 応答を送信することです。

独自のニーズに合わせてこの動作を上書きすることができます:

Flight::map('error', function (Throwable $error) {
  // エラーを処理する
  echo $error->getTraceAsString();
});

デフォルトでは、エラーはウェブサーバーに記録されません。これを有効にすることでログを取得できます:

Flight::set('flight.log_errors', true);

見つかりません

URL が見つからない場合、Flight は notFound メソッドを呼び出します。デフォルトの動作は、簡単なメッセージを含む HTTP 404 Not Found 応答を送信することです。

独自のニーズに合わせてこの動作を上書きすることができます:

Flight::map('notFound', function () {
  // 見つからない場合の処理
});

Learn/flight_vs_laravel

フライト vs ララベル

ララベルとは何ですか?

Laravel は、すべての機能が備わっており、素晴らしい開発者向けエコシステムを持つフル機能のフレームワークですが、パフォーマンスと複雑さにはコストがかかります。 Laravelの目標は、開発者が最高レベルの生産性を持ち、一般的なタスクを簡単に行えるようにすることです。 Laravelは、フル機能のエンタープライズ Web アプリケーションを構築しようとしている開発者にとって優れた選択肢です。 これには、パフォーマンスと複雑さの観点でいくつかのトレードオフが伴います。 Laravelの基本を学ぶことは簡単ですが、フレームワークを習得するには時間がかかるかもしれません。

開発者がしばしば問題を解決する唯一の方法はこれらのモジュールであるかのように感じるほど、Laravelモジュールが非常に多いですが、実際には別のライブラリを使用するか、独自のコードを書くこともできます。

フライトとの比較でのメリット

フライトとの比較でのデメリット

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);

利用可能な設定

以下は利用可能な設定の一覧です:

ローダーの設定

_ をクラス名に含める場合の追加のローダーの設定があります。これにより、クラスを自動的に読み込むことができます。

// アンダースコアを使用したクラスのローディングを有効にする
// デフォルトは true
Loader::$v2ClassLoading = false;

変数

Flight を使用すると、アプリケーション内のどこからでも使用できるように変数を保存できます。

// 変数を保存する
Flight::set('id', 123);

// アプリケーション内の別の場所で
$id = Flight::get('id');

変数が設定されているかどうかを確認するには、次のようにします:

if (Flight::has('id')) {
  // 何かを行う
}

次のようにして変数をクリアできます:

// id 変数をクリアする
Flight::clear('id');

// すべての変数をクリア
Flight::clear();

Flight は設定目的で変数も使用します。

Flight::set('flight.log_errors', true);

エラー処理

エラーと例外

すべてのエラーと例外は Flight によってキャッチされ、error メソッドに渡されます。デフォルトの動作は、一般的な HTTP 500 Internal Server Error 応答を送信し、いくつかのエラー情報を含めることです。

独自のニーズに合わせてこの動作をオーバーライドできます:

Flight::map('error', function (Throwable $error) {
  // エラーを処理する
  echo $error->getTraceAsString();
});

デフォルトでは、エラーはウェブサーバーにログ記録されていません。これを有効にできます:

Flight::set('flight.log_errors', true);

見つからない場合

URL が見つからない場合、Flight は notFound メソッドを呼び出します。デフォルトの動作は、簡単なメッセージを含む HTTP 404 Not Found 応答を送信することです。

独自のニーズに合わせてこの動作をオーバーライドできます:

Flight::map('notFound', function () {
  // 見つからなかった時の処理
});

Learn/ai

AI と開発者体験 with Flight

Flight は、より速く、より賢く、摩擦を少なくして構築するのを助けるものです。特に、AI 駆動のツールや現代の開発ワークフローで作業する場合です。このページでは、Flight がプロジェクトを AI で強化しやすくする方法、そしてフレームワークとスケルトンプロジェクトに組み込まれた新しい AI ヘルパーの使い方を説明します。


AI-Ready by Default: The Skeleton Project

公式の flightphp/skeleton スターターには、以下の人気の AI コーディングアシスタントの指示と設定が含まれています:

これらのツールは、プロジェクト固有の指示で事前に設定されているため、コードを書く際に最も関連性が高く、文脈を考慮した助けを得られます。つまり:

なぜこれが重要ですか?

AI ツールがプロジェクトの意図と規約を知っている場合、機能のスキャフォールディング、コードのリファクタリング、一般的なミスの回避を助けてくれます。これにより、初日からあなた(とあなたのチーム)がより生産的になります。


New AI Commands in Flight Core

v3.16.0+

Flight core には、プロジェクトを設定し、AI で導くのに役立つ 2 つの強力な CLI コマンドが含まれています:

1. ai:init — Connect to Your Favorite LLM Provider

このコマンドは、OpenAI、Grok、または Anthropic (Claude) などの LLM (Large Language Model) プロバイダーの資格情報を設定する手順を案内します。

Example:

php runway ai:init

プロバイダーの選択、API キーの入力、モデルの選択を求められます。これにより、プロジェクトを最新の AI サービスに簡単に接続できます—手動設定は不要です。

2. ai:generate-instructions — Project-Aware AI Coding Instructions

このコマンドは、プロジェクト固有の指示を AI コーディングアシスタント用に作成または更新します。プロジェクトの用途、使用するデータベース、チームの規模など、いくつかの簡単な質問をします。その後、LLM プロバイダーを使用して、調整された指示を生成します。

指示がすでに存在する場合、提供した回答を反映して更新します。これらの指示は自動的に以下に書き込まれます:

Example:

php runway ai:generate-instructions

なぜこれが役立つのですか?

最新のプロジェクト固有の指示があれば、AI ツールは次のようにできます:

  • より良いコードの提案を提供します
  • プロジェクトの独自のニーズを理解します
  • 新しい貢献者のオンボーディングを速めます
  • プロジェクトが進化するにつれて、摩擦と混乱を減らします

Not Just for Building AI Apps

AI 駆動の機能(例:チャットボット、スマート API、または統合)を構築するために Flight を使用することもできますが、真の強みは、開発者として AI ツールをより良く活用する点にあります。それは:


Learn More & Get Started

Learn/security

セキュリティ

セキュリティはウェブアプリケーションにとって非常に重要です。アプリケーションが安全で、ユーザーのデータが守られていることを確認したいでしょう。Flightは、ウェブアプリケーションのセキュリティを確保するための多くの機能を提供しています。

ヘッダー

HTTPヘッダーは、ウェブアプリケーションを保護する最も簡単な方法の1つです。ヘッダーを使用して、クリックジャッキング、XSS、およびその他の攻撃を防ぐことができます。これらのヘッダーをアプリケーションに追加する方法はいくつかあります。

ヘッダーのセキュリティを確認するための優れたウェブサイトは、securityheaders.comobservatory.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.phpindex.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);
// これは出力されます: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

// もしあなたがビュークラスとして登録された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();

mapregisterなどのフレームワークのメソッドはオーバーライドできません。これを試みるとエラーが発生します。

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/usersaliasBaseになります。これらのルートが作成されると、エイリアスはusers.indexusers.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 アプリケーションの作成とメンテナンスをスピードアップします。反復的なコーディングタスクを終了し、コードを制御する力を享受します。

フライトとの比較での長所

フライトとの比較での短所

Learn/flight_vs_another_framework

他のフレームワークとのFlightの比較

もし、Laravel、Slim、Fat-Free、あるいはSymfonyのような他のフレームワークからFlightに移行している場合、このページは両者の違いを理解するのに役立ちます。

Laravel

Laravelはベルと笛がすべて揃った充実したフレームワークで、驚くべき開発者中心のエコシステムを持っていますが、パフォーマンスと複雑さと引き換えになります。

LaravelとFlightの比較を見る.

Slim

SlimはFlightに似たマイクロフレームワークです。軽量で使いやすく設計されていますが、Flightよりも少し複雑になることがあります。

SlimとFlightの比較を見る.

Fat-Free

Fat-Freeはより小さなパッケージで提供されるフルスタックフレームワークです。ツールはすべてそろっていますが、いくつかのプロジェクトをより複雑にするデータアーキテクチャを持っています。

Fat-FreeとFlightの比較を見る.

Symfony

Symfonyはモジュール式のエンタープライズレベルのフレームワークであり、柔軟性と拡張性を備えています。より小さなプロジェクトや新人開発者にとって、Symfonyは少し抵抗があるかもしれません。

SymfonyとFlightの比較を見る.

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!」と表示されます。

ミドルウェアについて重要な注意事項がいくつかありますので、使用する前に認識しておく必要があります:

ミドルウェアクラス

ミドルウェアはクラスとしても登録できます。"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!」が表示されます。

ミドルウェアエラーの処理

認証ミドルウェアがあるとして、認証されていない場合にユーザーをログインページにリダイレクトしたいとします。その場合、次のオプションがいくつかあります:

  1. ミドルウェア関数からfalseを返すと、Flightは自動的に403 Forbiddenエラーを返しますが、カスタマイズはできません。
  2. Flight::redirect()を使用してユーザーをログインページにリダイレクトできます。
  3. ミドルウェア内でカスタムエラーを作成し、ルートの実行を停止できます。

基本的な例

次に、単純な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;
});

mapregister などのコアメソッドは、直接呼び出されて動的に呼び出されないため、フィルタリングすることはできません。

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を使ってデータベースに保存するか、何か他のことをする
});

リクエストオブジェクトのプロパティ

リクエストオブジェクトは以下のプロパティを提供します:

querydatacookies、および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レスポンスを送信します。

mapregisterで追加された任意のカスタムメソッドもフィルタリングできます。

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

フレームワークを使う理由

一部のプログラマーは、フレームワークの使用に熱烈に反対しています。フレームワークは膨大で、遅く、学習が困難だと主張しています。 彼らは、フレームワークは不要であり、それらなしでより良いコードを書くことができると述べています。 フレームワークを使用することのデメリットについては、いくつかの妥当なポイントがあります。 ただし、フレームワークを使用する利点もたくさんあります。

フレームワークを使用する理由

フレームワークを使用したいと思う理由のいくつかを以下に示します:

Flightはマイクロフレームワークです。 つまり、小さく軽量です。 LaravelやSymfonyのような大規模なフレームワークほどの機能は提供しません。 ただし、Webアプリケーションを構築するために必要な機能の多くを提供します。 また、学びやすく使用も容易です。 これにより、簡単かつ迅速にWebアプリケーションを構築するのに適しています。 フレームワークに新しい場合は、Flightは初心者に最適なフレームワークです。 フレームワークを使用する利点を学び、過度な複雑さで圧倒されることなく学習するのに役立ちます。 Flightの経験を積んだ後は、LaravelやSymfonyなどのより複雑なフレームワークに移ることがより簡単になります。 ただし、Flightでも成功した堅牢なアプリケーションを作成できます。

ルーティングとは?

ルーティングはFlightフレームワークの中核ですが、それは一体何でしょうか? ルーティングとは、URLを取得してコード内の特定の関数に一致させるプロセスです。 これにより、WebサイトをリクエストされたURLに基づいて異なる動作をさせることができます。 たとえば、ユーザーが/user/1234を訪れたときにユーザープロフィールを表示したいが、/usersを訪れたときに全ユーザーのリストを表示したいとします。 これはすべてルーティングを通じて行われます。

以下のようになります:

そして、なぜ重要なの?

適切な中央集権的なルーターを持つことで、あなたの生活が劇的に簡単になる可能性があります! 最初はそれが見えにくいかもしれません。 以下は、そのいくつか理由です:

多分、Webサイトを作成するためのスクリプトごとの方法に慣れているかもしれません。 index.phpというファイルがあり、URLを確認し、URLに基づいて特定の関数を実行します。 これもルーティングの一形態ですが、整理されていない上にすぐに手に負えなくなります。 Flightのルーティングシステムは、ルーティングを処理するより整理された強力な方法です。

これ?


// /user/view_profile.php?id=1234
if ($_GET['id']) {
    $id = $_GET['id'];
    viewUserProfile($id);
}

// /user/edit_profile.php?id=1234
if ($_GET['id']) {
    $id = $_GET['id'];
    editUserProfile($id);
}

// etc...

それともこちら?


// index.php
Flight::route('/user/@id', [ 'UserController', 'viewUserProfile' ]);
Flight::route('/user/@id/edit', [ 'UserController', 'editUserProfile' ]);

// In maybe your app/controllers/UserController.php
class UserController {
    public function viewUserProfile($id) {
        // do something
    }

    public function editUserProfile($id) {
        // do something
    }
}

中央集権的なルーティングシステムを使用する利点が見え始めたことを願っています。 長い目で見れば、管理や理解が容易になります!

リクエストとレスポンス

Flightはリクエストとレスポンスを処理するための簡単で簡単な方法を提供します。 これがWebフレームワークの中核です。 ユーザーのブラウザからのリクエストを受け取り、処理してからレスポンスを返すことで、Webアプリケーションを構築できます。 これにより、ユーザープロフィールを表示したり、ユーザーのログインを許可したり、新しいブログ投稿を許可したりするWebアプリケーションを構築できます。

リクエスト

リクエストは、ユーザーのブラウザがウェブサイトを訪れるときにサーバーに送信するものです。 このリクエストには、ユーザーが何をしたいかに関する情報が含まれます。 たとえば、ユーザーが訪れたいURLの情報、ユーザーがサーバーに送りたいデータ、サーバーから受け取りたいデータの種類などが含まれるかもしれません。 リクエストは読み取り専用です。 リクエストを変更することはできませんが、読み取ることはできます。

Flightはリクエストに関する情報にアクセスするための簡単な方法を提供します。 Flight::request()メソッドを使用してリクエストに関する情報にアクセスできます。 このメソッドはリクエストに関する情報を含むRequestオブジェクトを返します。 このオブジェクトを使用して、URL、メソッド、ユーザーがサーバーに送信したデータなどの情報にアクセスできます。

レスポンス

レスポンスとは、ユーザーのブラウザがウェブサイトを訪れるときにサーバーがユーザーのブラウザに送り返すものです。 このレスポンスには、サーバーが行いたいことに関する情報が含まれます。 たとえば、サーバーがユーザーに送信したいデータの種類、ユーザーから受け取りたいデータの種類、サーバーがユーザーのコンピュータに保存したいデータの種類などが含まれるかもしれません。

Flightはユーザーのブラウザにレスポンスを送信する簡単な方法を提供します。 Flight::response()メソッドを使用してレスポンスを送信できます。 このメソッドは、Responseオブジェクトを引数として受け取り、そのレスポンスをユーザーのブラウザに送信します。 このオブジェクトを使用して、HTML、JSON、ファイルなど、ユーザーのブラウザにレスポンスを送信できます。 Flightはレスポンスの一部を自動生成して簡単にするお手伝いをしますが、最終的にはユーザーに送り返す内容を制御できます。

Learn/httpcaching

HTTP キャッシング

Flight は HTTP レベルのキャッシングを組み込みでサポートしています。キャッシングの条件を満たすと、Flight は HTTP 304 Not Modified のレスポンスを返します。クライアントが同じリソースを次にリクエストするときは、ローカルにキャッシュされたバージョンを使用するよう促されます。

Last-Modified

lastModified メソッドを使用して、UNIX タイムスタンプを渡すことでページが最後に変更された日時を設定できます。クライアントは最終変更日時の値が変更されるまで、キャッシュを引き続き使用します。

Flight::route('/news', function () {
  Flight::lastModified(1234567890);
  echo 'このコンテンツはキャッシュされます。';
});

ETag

ETag キャッシングは Last-Modified と似ていますが、リソースに任意の ID を指定できます:

Flight::route('/news', function () {
  Flight::etag('my-unique-id');
  echo 'このコンテンツはキャッシュされます。';
});

lastModified または etag のいずれかを呼び出すと、キャッシュの値が設定されてチェックされます。リクエスト間でキャッシュの値が同じ場合、Flight は直ちに HTTP 304 レスポンスを送信して処理を停止します。

Learn/responses

レスポンス

Flight はあなたのためにレスポンスヘッダーの一部を生成しますが、ユーザーに返すものの大部分はあなたが制御します。場合によっては、Response オブジェクトに直接アクセスできますが、ほとんどの場合、Flight インスタンスを使用してレスポンスを送信します。

基本的なレスポンスの送信

Flight は ob_start() を使用して出力をバッファリングします。これは、echo または print を使用してユーザーにレスポンスを送信でき、Flight がそれをキャプチャして適切なヘッダーでユーザーに返すことを意味します。


// これにより「Hello, World!」がユーザーのブラウザに送信されます
Flight::route('/', function() {
    echo "Hello, World!";
});

// HTTP/1.1 200 OK
// Content-Type: text/html
//
// Hello, World!

代わりに、write() メソッドを呼び出してボディに追加することも可能です。


// これにより「Hello, World!」がユーザーのブラウザに送信されます
Flight::route('/', function() {
    // 冗長ですが、必要なときに仕事をすることがあります
    Flight::response()->write("Hello, World!");

    // この時点で設定したボディを取得したい場合
    // このようにして取得できます
    $body = Flight::response()->getBody();
});

ステータスコード

status メソッドを使用してレスポンスのステータスコードを設定できます:

Flight::route('/@id', function($id) {
    if($id == 123) {
        Flight::response()->status(200);
        echo "Hello, World!";
    } else {
        Flight::response()->status(403);
        echo "Forbidden";
    }
});

現在のステータスコードを取得したい場合は、引数なしで status メソッドを使用できます:

Flight::response()->status(); // 200

レスポンスボディの設定

write メソッドを使用してレスポンスボディを設定できますが、何かを echo または print すると それはキャプチャされ、出力バッファリング経由でレスポンスボディとして送信されます。

Flight::route('/', function() {
    Flight::response()->write("Hello, World!");
});

// 同じ意味です

Flight::route('/', function() {
    echo "Hello, World!";
});

レスポンスボディのクリア

レスポンスボディをクリアしたい場合は、clearBody メソッドを使用できます:

Flight::route('/', function() {
    if($someCondition) {
        Flight::response()->write("Hello, World!");
    } else {
        Flight::response()->clearBody();
    }
});

レスポンスボディでコールバックを実行

addResponseBodyCallback メソッドを使用してレスポンスボディでコールバックを実行できます:

Flight::route('/users', function() {
    $db = Flight::db();
    $users = $db->fetchAll("SELECT * FROM users");
    Flight::render('users_table', ['users' => $users]);
});

// これにより、すべてのルートのレスポンスが gzip 圧縮されます
Flight::response()->addResponseBodyCallback(function($body) {
    return gzencode($body, 9);
});

複数のコールバックを追加でき、それらは追加された順に実行されます。このメソッドは任意の callable を受け入れるため、クラス配列 [ $class, 'method' ]、クロージャ $strReplace = function($body) { str_replace('hi', 'there', $body); };、または例えばHTMLコードを最適化する関数名 'minify' を受け入れることができます。

注意: ルートコールバックは flight.v2.output_buffering 設定オプションを使用している場合には機能しません。

特定のルートコールバック

これを特定のルートにのみ適用したい場合は、ルート自体でコールバックを追加できます:

Flight::route('/users', function() {
    $db = Flight::db();
    $users = $db->fetchAll("SELECT * FROM users");
    Flight::render('users_table', ['users' => $users]);

    // これにより、このルートのレスポンスのみが gzip 圧縮されます
    Flight::response()->addResponseBodyCallback(function($body) {
        return gzencode($body, 9);
    });
});

ミドルウェアオプション

ミドルウェアを使用してすべてのルートにコールバックを適用することもできます:

// MinifyMiddleware.php
class MinifyMiddleware {
    public function before() {
        // レスポンス() オブジェクトにここでコールバックを適用します。
        Flight::response()->addResponseBodyCallback(function($body) {
            return $this->minify($body);
        });
    }

    protected function minify(string $body): string {
        // ボディを何らかの方法で最適化します
        return $body;
    }
}

// index.php
Flight::group('/users', function() {
    Flight::route('', function() { /* ... */ });
    Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);

レスポンスヘッダーの設定

header メソッドを使用してレスポンスのコンテンツタイプなどのヘッダーを設定できます:


// これにより「Hello, World!」がプレーンテキストでユーザーのブラウザに送信されます
Flight::route('/', function() {
    Flight::response()->header('Content-Type', 'text/plain');
    // または
    Flight::response()->setHeader('Content-Type', 'text/plain');
    echo "Hello, World!";
});

JSON

Flight は JSON および JSONP レスポンスを送信するためのサポートを提供しています。JSON レスポンスを送信するには、いくつかのデータを JSON エンコードします:

Flight::json(['id' => 123]);

注意: デフォルトで、Flight はレスポンスに Content-Type: application/json ヘッダーを送信します。また、JSON をエンコードするときに定数 JSON_THROW_ON_ERROR および JSON_UNESCAPED_SLASHES を使用します。

ステータスコード付きの JSON

第二引数としてステータスコードを渡すこともできます:

Flight::json(['id' => 123], 201);

プレティプリント付きの JSON

最後の位置に引数を渡すことで、プレティプリントを有効にすることも可能です:

Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);

Flight::json() に渡すオプションを変更して、よりシンプルな構文が必要な場合は、JSON メソッドを再マッピングすることができます:

Flight::map('json', function($data, $code = 200, $options = 0) {
    Flight::_json($data, $code, true, 'utf-8', $options);
}

// これで、このように使うことができます
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);

JSON と実行の停止 (v3.10.0)

JSON レスポンスを送信し実行を停止する場合は、jsonHalt メソッドを使用します。 これは、何らかの認証をチェックしていて、ユーザーが認証されていない場合に、すぐに JSON レスポンスを送信し、既存のボディコンテンツをクリアし、実行を停止できる便利な方法です。

Flight::route('/users', function() {
    $authorized = someAuthorizationCheck();
    // ユーザーが認証されているかどうかをチェック
    if($authorized === false) {
        Flight::jsonHalt(['error' => 'Unauthorized'], 401);
    }

    // ルートの残りを続行
});

v3.10.0以前では、このようにする必要がありました:

Flight::route('/users', function() {
    $authorized = someAuthorizationCheck();
    // ユーザーが認証されているかどうかをチェック
    if($authorized === false) {
        Flight::halt(401, json_encode(['error' => 'Unauthorized']));
    }

    // ルートの残りを続行
});

JSONP

JSONP リクエストについては、オプションでコールバック関数を定義するために使用しているクエリパラメータ名を渡すことができます:

Flight::jsonp(['id' => 123], 'q');

したがって、?q=my_func を使用して GET リクエストを行うと、次の出力が得られます:

my_func({"id":123});

クエリパラメータ名を渡さない場合は、デフォルトで jsonp に設定されます。

別の URL へのリダイレクト

redirect() メソッドを使用して新しい URL を渡すことで、現在のリクエストをリダイレクトできます:

Flight::redirect('/new/location');

デフォルトでは Flight は HTTP 303 ("See Other") ステータスコードを送信します。オプションでカスタムコードを設定できます:

Flight::redirect('/new/location', 401);

停止

いつでも halt メソッドを呼び出すことでフレームワークを停止できます:

Flight::halt();

オプションの HTTP ステータスコードとメッセージを指定することもできます:

Flight::halt(200, 'Be right back...');

halt を呼び出すと、その時点までのレスポンスコンテンツは破棄されます。フレームワークを停止し、現在のレスポンスを出力したい場合は、stop メソッドを使用します:

Flight::stop();

レスポンスデータのクリア

clear() メソッドを使用してレスポンスボディとヘッダーをクリアできます。これにより、レスポンスに割り当てられたヘッダーがクリアされ、レスポンスボディがクリアされ、ステータスコードが 200 に設定されます。

Flight::response()->clear();

レスポンスボディのみのクリア

レスポンスボディのみをクリアしたい場合は、clearBody() メソッドを使用できます:

// これにより、レスポンス() オブジェクトに設定されたヘッダーは保持されます。
Flight::response()->clearBody();

HTTP キャッシング

Flight には、HTTP レベルのキャッシングに対するビルトインのサポートがあります。キャッシング条件が満たされると、Flight は HTTP 304 Not Modified レスポンスを返します。クライアントが同じリソースを再度リクエストする際には、ローカルにキャッシュされたバージョンを使用するように促されます。

ルートレベルのキャッシング

レスポンス全体をキャッシュしたい場合は、cache() メソッドを使用し、キャッシュする時間を渡すことができます。


// これにより、レスポンスが5分間キャッシュされます
Flight::route('/news', function () {
  Flight::response()->cache(time() + 300);
  echo 'このコンテンツはキャッシュされます。';
});

// または、strtotime() メソッドに渡す文字列を使用できます
Flight::route('/news', function () {
  Flight::response()->cache('+5 minutes');
  echo 'このコンテンツはキャッシュされます。';
});

最終更新日時

lastModified メソッドを使用して、UNIX タイムスタンプを渡し、ページが最後に修正された日時を設定できます。クライアントは、最終更新値が変更されるまでキャッシュを使用し続けます。

Flight::route('/news', function () {
  Flight::lastModified(1234567890);
  echo 'このコンテンツはキャッシュされます。';
});

ETag

ETag キャッシングは Last-Modified と似ていますが、リソースのために望む任意の ID を指定できます:

Flight::route('/news', function () {
  Flight::etag('my-unique-id');
  echo 'このコンテンツはキャッシュされます。';
});

lastModified または etag を呼び出すと、どちらもキャッシュ値を設定およびチェックします。要求間でキャッシュ値が同じである場合、Flight は即座に HTTP 304 レスポンスを送信し、処理を停止します。

ファイルのダウンロード (v3.12.0)

ファイルをダウンロードするためのヘルパーメソッドがあります。download メソッドを使用し、パスを渡すことができます。

Flight::route('/download', function () {
  Flight::download('/path/to/file.txt');
});

Learn/frameworkinstance

フレームワークのインスタンス

Flightをグローバルな静的クラスとして実行する代わりに、オブジェクトのインスタンスとして実行することもできます。

require 'flight/autoload.php';

$app = Flight::app();

$app->route('/', function () {
  echo 'hello world!';
});

$app->start();

静的なメソッドを呼び出す代わりに、同じ名前のインスタンスメソッドをEngineオブジェクトで呼び出すことになります。

Learn/redirects

リダイレクト

redirect メソッドを使用して、新しい URL を指定して現在のリクエストをリダイレクトできます:

Flight::redirect('/new/location');

Flight はデフォルトで HTTP 303 ステータスコードを送信します。オプションでカスタムコードを設定できます:

Flight::redirect('/new/location', 401);

Learn/events

Flight PHP のイベントシステム (v3.15.0+)

Flight PHP は、軽量で直感的なイベントシステムを導入し、アプリケーションでカスタムイベントを登録およびトリガーできます。Flight::onEvent()Flight::triggerEvent() の追加により、アプリのライフサイクルにおける重要なタイミングにフックしたり、自分でイベントを定義したりして、コードをよりモジュール化し拡張しやすくできます。これらのメソッドは Flight の マップ可能なメソッド であり、必要に応じて動作をオーバーライドできます。

このガイドでは、イベントの基礎知識から、なぜそれらが有用か、使い方、実践的な例までをカバーし、初心者がその力を理解する手助けをします。

なぜイベントを使うのか?

イベントを使うことで、アプリケーションの異なる部分を互いに過度に依存しないように分離できます。この分離(デカップリング と呼ばれる)は、コードの更新、拡張、デバッグを容易にします。一つの大きな塊で全てを書く代わりに、特定のアクション(イベント)に応答する小さな独立した部分にロジックを分割できます。

ブログアプリを作成していると想像してください:

イベントを使わずにこれらを一つの関数に詰め込むことになりますが、イベントを使うと分割できます:一つの部分でコメントを保存し、もう一つの部分で 'comment.posted' というイベントをトリガーし、別のリスナーがメール送信とログを処理します。これにより、コードがクリーンになり、機能(例: 通知)を追加または削除する際にコアロジックを触らずに済みます。

一般的な用途

イベントリスナーの登録

イベントをリッスンするには Flight::onEvent() を使います。このメソッドでイベントが発生したときに何が起こるかを定義します。

構文

// イベント名とコールバックを指定します
Flight::onEvent(string $event, callable $callback): void

動作の仕組み

イベントに「購読」することで、発生したときに何をするかを Flight に伝えます。コールバックはイベントトリガーから渡された引数を受け取ることができます。

Flight のイベントシステムは同期型です。つまり、各イベントリスナーは順番に実行され、すべての登録されたリスナーが完了するまでコードの実行が続きます。これは非同期のイベントシステムとは異なり、リスナーが並行して実行されたり後で実行されたりしない点が重要です。

簡単な例

// 'user.login' イベントがトリガーされたら、ユーザーを挨拶します
Flight::onEvent('user.login', function ($username) {
    echo "Welcome back, $username!";
});

ここで、'user.login' イベントがトリガーされると、ユーザーの名前で挨拶します。

重要なポイント

イベントのトリガー

イベントを発生させるには Flight::triggerEvent() を使います。これにより、登録されたリスナーを実行し、必要なデータを渡します。

構文

// イベント名と任意の引数を指定します
Flight::triggerEvent(string $event, ...$args): void

簡単な例

$username = 'alice';
Flight::triggerEvent('user.login', $username);

これは 'user.login' イベントをトリガーし、'alice' を先に定義したリスナーに渡します。結果として、Welcome back, alice! と出力されます。

重要なポイント

イベントリスナーの登録

...

さらなるリスナーの停止: リスナーが false を返す場合、そのイベントの追加のリスナーは実行されなくなります。これにより、特定の条件に基づいてイベントチェーンを停止できます。リスナーの順序が重要で、最初に false を返すものが残りの実行を止めます。

:

// ユーザーが禁止されている場合、ログアウトし、以降のリスナーを停止します
Flight::onEvent('user.login', function ($username) {
    if (isBanned($username)) {
        logoutUser($username);
        return false; // 以降のリスナーを停止
    }
});
Flight::onEvent('user.login', function ($username) {
    sendWelcomeEmail($username); // これは実行されません
});

イベントメソッドのオーバーライド

Flight::onEvent()Flight::triggerEvent()拡張 可能で、動作を再定義できます。これはイベントシステムをカスタマイズしたい上級ユーザーに便利です。例えば、ログの追加やイベントのディスパッチ方法の変更などです。

例: onEvent のカスタマイズ

// イベント登録をログに記録します
Flight::map('onEvent', function (string $event, callable $callback) {
    // 毎回のイベント登録をログに記録
    error_log("New event listener added for: $event");
    // 内部のデフォルト動作を呼び出す (内部イベントシステムを仮定)
    Flight::_onEvent($event, $callback);
});

今度は、イベントを登録するたびにログが記録されます。

なぜオーバーライドするのか?

イベントをどこに置くか

初心者の方は、アプリでこれらのイベントをどこに登録するのか? と疑問に思うかもしれません。Flight のシンプルさから厳格なルールはありませんが、整理しておくことでアプリが成長してもコードを維持しやすくなります。以下は実践的なオプションとベストプラクティスで、Flight の軽量性を考慮しています:

オプション 1: メインの index.php ファイル内

小さなアプリやクイックプロトタイプの場合、イベントを index.php ファイルにルートと一緒に登録できます。これにより全てが一つの場所にまとまり、シンプルさを優先できます。

require 'vendor/autoload.php';

// イベントを登録
Flight::onEvent('user.login', function ($username) {
    error_log("$username logged in at " . date('Y-m-d H:i:s'));
});

// ルートを定義
Flight::route('/login', function () {
    $username = 'bob';
    Flight::triggerEvent('user.login', $username);
    echo "Logged in!";
});

Flight::start();

オプション 2: 別々の events.php ファイル

少し大きなアプリの場合、イベント登録を app/config/events.php のような専用ファイルに移動し、index.php でインクルードします。これは Flight プロジェクトでルートを整理するのと同じアプローチです。

// app/config/events.php
Flight::onEvent('user.login', function ($username) {
    error_log("$username logged in at " . date('Y-m-d H:i:s'));
});

Flight::onEvent('user.registered', function ($email, $name) {
    echo "Email sent to $email: Welcome, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';

Flight::route('/login', function () {
    $username = 'bob';
    Flight::triggerEvent('user.login', $username);
    echo "Logged in!";
});

Flight::start();

オプション 3: トリガーされる場所の近く

もう一つのアプローチは、イベントをトリガーされる場所に近い、例えばコントローラーやルート定義内に登録するものです。これがアプリの特定の部分に特化している場合に有効です。

Flight::route('/signup', function () {
    // ここでイベントを登録
    Flight::onEvent('user.registered', function ($email) {
        echo "Welcome email sent to $email!";
    });

    $email = 'jane@example.com';
    Flight::triggerEvent('user.registered', $email);
    echo "Signed up!";
});

Flight 向けのベストプラクティス

ヒント: 目的ごとにグループ化

events.php では、関連するイベントをコメント付きでグループ化:

// app/config/events.php
// ユーザー関連のイベント
Flight::onEvent('user.login', function ($username) {
    error_log("$username logged in");
});
Flight::onEvent('user.registered', function ($email) {
    echo "Welcome to $email!";
});

// ページ関連のイベント
Flight::onEvent('page.updated', function ($pageId) {
    unset($_SESSION['pages'][$pageId]);
});

この構造はスケーラブルで、初心者にも親しみやすい。

初心者向けの例

実際のシナリオを通じて、イベントがどのように動作し、なぜ役立つかを説明します。

例 1: ユーザーログインのログ記録

// ステップ 1: リスナーを登録
Flight::onEvent('user.login', function ($username) {
    $time = date('Y-m-d H:i:s');
    error_log("$username logged in at $time");
});

// ステップ 2: アプリ内でトリガー
Flight::route('/login', function () {
    $username = 'bob'; // フォームから取得したと仮定
    Flight::triggerEvent('user.login', $username);
    echo "Hi, $username!";
});

なぜ役立つか: ログインコードはログについて知らなくてもイベントをトリガーするだけです。後でリスナーを追加 (例: ウェルカムメール) してもルートを変更せずに済みます。

例 2: 新規ユーザーの通知

// 新規登録のリスナー
Flight::onEvent('user.registered', function ($email, $name) {
    // メール送信をシミュレート
    echo "Email sent to $email: Welcome, $name!";
});

// サインアップ時にトリガー
Flight::route('/signup', function () {
    $email = 'jane@example.com';
    $name = 'Jane';
    Flight::triggerEvent('user.registered', $email, $name);
    echo "Thanks for signing up!";
});

なぜ役立つか: サインアップロジックはユーザー作成に集中し、イベントが通知を処理します。後でリスナーを追加 (例: サインアップのログ) できます。

例 3: キャッシュのクリア

// キャッシュをクリアするリスナー
Flight::onEvent('page.updated', function ($pageId) {
    unset($_SESSION['pages'][$pageId]); // セッションキャッシュをクリア
    echo "Cache cleared for page $pageId.";
});

// ページを編集したときにトリガー
Flight::route('/edit-page/(@id)', function ($pageId) {
    // ページを更新したと仮定
    Flight::triggerEvent('page.updated', $pageId);
    echo "Page $pageId updated.";
});

なぜ役立つか: 編集コードはキャッシングを気にせず、更新をシグナルするだけです。他の部分が対応できます。

ベストプラクティス

Flight PHP のイベントシステムは、Flight::onEvent()Flight::triggerEvent() により、シンプルでありながら強力な柔軟なアプリケーション構築を可能にします。イベントを通じてアプリの異なる部分が通信することで、コードを整理し、再利用しやすく、拡張しやすくします。アクションのログ、通知の送信、更新の管理など、イベントがあればロジックを絡ませずに実現できます。さらに、これらのメソッドをオーバーライドできるので、システムをニーズに合わせてカスタマイズできます。単一のイベントから始め、アプリの構造がどのように変わるかを見てください!

ビルトインイベント

Flight PHP には、フレームワークのライフサイクルにフックするためのビルトインイベントがいくつか用意されています。これらのイベントは、リクエスト/レスポンスサイクルの特定のタイミングでトリガーされ、カスタムロジックを実行できます。

ビルトインイベント一覧

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');

その後、ビューには headerContentbodyContent という名前の保存された変数があります。次に、次のようにしてレイアウトをレンダリングできます:

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');

これにより、headerContentbodyContent という名前の保存された変数を持つことができます。そして、次のようにしてレイアウトをレンダリングできます:

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

Flightと比較したCons

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';
});

また、beforeafterメソッドを使用して基本的な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();

ただし、mapregisterのようなフレームワークメソッドはオーバーライドできません。そうしようとするとエラーが発生します。

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と比較したメリット

Flightと比較したデメリット

Learn/autoloading

オートローディング

オートローディングは、PHPにおいてクラスを読み込むディレクトリを指定する概念です。これは、requireincludeを使用してクラスをロードするよりも有益です。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のシンプルさと柔軟性を示しています。最後には、ホームページ、個別の投稿ページ、および作成フォームを持つ機能的なブログが完成します。

前提条件

ステップ1: プロジェクトのセットアップ

新しいプロジェクトディレクトリを作成し、Composerを介してFlightをインストールします。

  1. ディレクトリの作成:

    mkdir flight-blog
    cd flight-blog
  2. Flightのインストール:

    composer require flightphp/core
  3. パブリックディレクトリの作成: Flightは単一のエントリーポイント(index.php)を使用します。それ用にpublic/フォルダを作成します:

    mkdir public
  4. 基本的なindex.php: シンプルな「Hello World」ルートを持つpublic/index.phpを作成します:

    <?php
    require '../vendor/autoload.php';
    
    Flight::route('/', function () {
       echo 'こんにちは、Flight!';
    });
    
    Flight::start();
  5. 組み込みサーバーの起動: PHPの開発サーバーを使用してセットアップをテストします:

    php -S localhost:8000 -t public/

    http://localhost:8000にアクセスして「こんにちは、Flight!」を見ることができます。

ステップ2: プロジェクト構造の整理

クリーンなセットアップのために、プロジェクトを以下のように構成します:

flight-blog/
├── app/
│   ├── config/
│   └── views/
├── data/
├── public/
│   └── index.php
├── vendor/
└── composer.json

ステップ3: Latteのインストールと設定

Latteは、Flightとよく統合される軽量なテンプレーティングエンジンです。

  1. Latteのインストール:

    composer require latte/latte
  2. 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();
  3. レイアウトテンプレートを作成する: 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>&copy; {date('Y')} Flightブログ</p>
    </footer>
    </body>
    </html>
  4. ホームテンプレートを作成: 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にアクセスしてレンダリングされたページを確認してください。

  5. データファイルを作成:

    簡単のためにデータベースのシミュレーションとしてJSONファイルを使用します。

    data/posts.jsonで:

    [
       {
           "slug": "first-post",
           "title": "私の最初の投稿",
           "content": "これはFlight PHPを使用した私の初めてのブログ投稿です!"
       }
    ]

ステップ4: ルートの定義

ルートを構成ファイルに分けることで、整理を良くしましょう。

  1. 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' => '投稿を作成']);
    });
  2. 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: ブログ投稿の保存と取得

投稿を読み込み、保存するメソッドを追加します。

  1. 投稿メソッドを追加: index.phpで、投稿を読み込むメソッドを追加します:

    Flight::map('posts', function () {
       $file = __DIR__ . '/../data/posts.json';
       return json_decode(file_get_contents($file), true);
    });
  2. ルートの更新: 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: テンプレートの作成

投稿を表示するためにテンプレートを更新します。

  1. 投稿ページ(app/views/post.latte:

    {extends 'layout.latte'}
    
    {block content}
        <h2>{$post['title']}</h2>
        <div class="post-content">
            <p>{$post['content']}</p>
        </div>
    {/block}

ステップ7: 投稿作成の追加

新しい投稿を追加するためのフォーム送信を処理します。

  1. フォーム(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}
  2. 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('/');
    });
  3. テストする:

    • 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}

次のステップ

結論

Flight PHPを使ってシンプルなブログを構築しました! このガイドでは、ルーティング、Latteによるテンプレーティング、およびフォーム送信の処理などのコア機能を示しました。すべてを軽量に保ちながら実施しています。さらにブログを進化させるためにFlightのドキュメントを探求してください!

License

The MIT License (MIT)

Copyright © 2024 @mikecao, @n0nag0n

個人が複製の許可を得ることができるように、このソフトウェアおよび関連ドキュメントファイル(以下「ソフトウェア」という)のコピーを入手することができます。 ソフトウェアを使用、コピー、変更、マージ、公開、配布、サブライセンス、販売する権利などを含む、制限なしでソフトウェアを扱う権利が、以下の条件に従って人々にそれを許可します:

上記の著作権表示およびこの許諾表示は、ソフトウェアのすべての複製または実質的な部分に含まれている必要があります。

ソフトウェアは、「現状有姿」で提供され、商品性、特定目的への適合性、および権利侵害を含むがこれに限定されない、いかなる種類の保証もなしに提供されます。 著作者または著作権保持者は、ソフトウェアまたは使用または他の取引に起因する契約上の行為、不法行為、その他の行為から生じるクレーム、損害、その他の責任について一切責任を負いません。

About

Flight とは?

Flight は、速く、シンプルで、拡張可能な PHP フレームワークです。開発者が素早く作業を完了させたい場合に最適で、一切の面倒なことを避けられます。クラシックなウェブアプリ、驚異的に速い API、または最新の AI 駆動ツールの実験など、Flight の低負荷で直感的な設計は、さまざまな用途にぴったりです。

Flight を選ぶ理由?

クイックスタート

まず、Composer でインストールします:

composer require flightphp/core

または、リポジトリの ZIP を こちら からダウンロードできます。次に、基本的な index.php ファイルを作成します:

<?php

// Composer でインストールした場合
require 'vendor/autoload.php';
// または ZIP ファイルで手動インストールした場合
// require 'flight/Flight.php';

Flight::route('/', function() {
  echo 'hello world!';
});

Flight::route('/json', function() {
  Flight::json(['hello' => 'world']);
});

Flight::start();

以上です!これで基本的な Flight アプリケーションが完成します。このファイルを php -S localhost:8000 で実行し、ブラウザで http://localhost:8000 を訪れると、出力を確認できます。

十分シンプルですね?
Flight のドキュメントでさらに学ぼう!

速いですか?

もちろんです! Flight は、PHP フレームワークの中でも最も速いもののひとつです。軽量なコアにより、オーバーヘッドが少なく、速度が向上します。これは、伝統的なアプリや現代の AI 駆動プロジェクトに最適です。ベンチマークは TechEmpower で確認できます。

以下は、他の人気の PHP フレームワークとのベンチマークです。

Framework Plaintext Reqs/sec JSON Reqs/sec
Flight 190,421 182,491
Yii 145,749 131,434
Fat-Free 139,238 133,952
Slim 89,588 87,348
Phalcon 95,911 87,675
Symfony 65,053 63,237
Lumen 40,572 39,700
Laravel 26,657 26,901
CodeIgniter 20,628 19,901

スケルトン/ボイラープレートアプリ

Flight の開始に役立つ例のアプリがあります。flightphp/skeleton を確認して、すぐに使えるプロジェクトを入手するか、examples ページでインスピレーションを得てください。AI の統合に興味がある場合? AI 駆動の例を探す

コミュニティ

Matrix Chat で参加できます

Matrix

そして Discord も

コントリビュート

Flight に貢献する方法は 2 つあります:

  1. コアフレームワークに貢献する: core repository を訪れてください。
  2. ドキュメントを改善する! このドキュメントウェブサイトは Github でホストされています。エラーを発見したり、改善したい場合、プルリクエストを送信してください。更新や新しいアイデア、特に AI と新技術に関するものを大歓迎です!

必要条件

Flight には PHP 7.4 以上が必要です。

注意: PHP 7.4 は、2024 年現在でいくつかの LTS Linux ディストリビューションのデフォルトバージョンであるため、サポートされています。PHP >8 への移行を強制すると、ユーザーに問題を引き起こす可能性があるためです。フレームワークは PHP >8 もサポートしています。

ライセンス

Flight は MIT ライセンスで公開されています。

Awesome-plugins/php_cookie

クッキー

overclokk/cookie はアプリ内でクッキーを管理するためのシンプルなライブラリです。

インストール

composerを使用して簡単にインストールできます。

composer require overclokk/cookie

使用法

使用法は、Flightクラスに新しいメソッドを登録するだけです。


use Overclokk\Cookie\Cookie;

/*
 * ブートストラップまたはpublic/index.phpファイルに設定
 */

Flight::register('cookie', Cookie::class);

/**
 * ExampleController.php
 */

class ExampleController {
    public function login() {
        // クッキーを設定します

        // インスタンスを取得するためfalseである必要があります
        // オートコンプリートを有効にしたい場合は以下のコメントを使用してください
        /** @var \Overclokk\Cookie\Cookie $cookie */
        $cookie = Flight::cookie(false);
        $cookie->set(
            'stay_logged_in', // クッキーの名前
            '1', // 設定したい値
            86400, // クッキーの有効期間(秒)
            '/', // クッキーが利用可能なパス
            'example.com', // クッキーが利用可能なドメイン
            true, // セキュアな HTTPS 接続でのみクッキーが送信されます
            true // クッキーはHTTPプロトコルを介してのみ利用可能です
        );

        // オプションで、デフォルト値を維持したい場合や、
        // 長期間にわたってクッキーを簡単に設定したい場合
        $cookie->forever('stay_logged_in', '1');
    }

    public function home() {
        // クッキーがあるかどうかをチェック
        if (Flight::cookie()->has('stay_logged_in')) {
            // 例えば、ダッシュボードエリアにリダイレクトします。
            Flight::redirect('/dashboard');
        }
    }
}

Awesome-plugins/php_encryption

PHP 暗号化

defuse/php-encryption はデータの暗号化と復号を行うために使用できるライブラリです。すぐにデータの暗号化と復号を始めることはかなり簡単です。ライブラリの使用方法や暗号化に関連する重要なセキュリティの問題を説明する素晴らしいtutorialがあります。

インストール

composerを使用して簡単にインストールします。

composer require defuse/php-encryption

セットアップ

その後、暗号化キーを生成する必要があります。

vendor/bin/generate-defuse-key

これにより、安全に保持する必要があるキーが生成されます。キーは、ファイルの末尾にある配列内のapp/config/config.phpファイルに保存できます。完璧な場所ではありませんが、少なくとも何かです。

使用方法

ライブラリと暗号化キーがあるので、データの暗号化と復号を開始できます。


use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;

/*
 * ブートストラップまたはpublic/index.phpファイルに設定します
 */

// 暗号化メソッド
Flight::map('encrypt', function($raw_data) {
    $encryption_key = /* $config['encryption_key']またはキーを配置した場所のfile_get_contents */;
    return Crypto::encrypt($raw_data, Key::loadFromAsciiSafeString($encryption_key));
});

// 復号メソッド
Flight::map('decrypt', function($encrypted_data) {
    $encryption_key = /* $config['encryption_key']またはキーを配置した場所のfile_get_contents */;
    try {
        $raw_data = Crypto::decrypt($encrypted_data, Key::loadFromAsciiSafeString($encryption_key));
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
        // 攻撃! 間違ったキーが読み込まれたか、暗号文が作成されてから変更された可能性があります -- データベースで破損されたか、攻撃を実行しようとするEveによって意図的に変更された可能性があります。

        // ... アプリケーションに適した方法でこのケースを処理します ...
    }
    return $raw_data;
});

Flight::route('/encrypt', function() {
    $encrypted_data = Flight::encrypt('これは秘密です');
    echo $encrypted_data;
});

Flight::route('/decrypt', function() {
    $encrypted_data = '...'; // どこかから暗号化されたデータを取得します
    $decrypted_data = Flight::decrypt($encrypted_data);
    echo $decrypted_data;
});

Awesome-plugins/php_file_cache

flightphp/cache

軽量でシンプルなスタンドアロンPHPインファイルキャッシュクラス

利点

このドキュメントサイトは、このライブラリを使用して各ページをキャッシュしています!

コードを表示するにはこちらをクリックしてください。

インストール

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

主要な設定オプション:

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と使用できるキャッシュライブラリがいくつかあります。

デバッグ

開発を行うローカル環境ではデバッグが重要です。いくつかのプラグインを使用するとデバッグ体験を向上させることができます。

データベース

データベースはほとんどのアプリケーションの中心です。これによりデータの保存と取得が可能になります。一部のデータベースライブラリはクエリの記述や実行を簡素化するラッパーであり、一部は完全なORMです。

セッション

APIにはあまり役立たないが、Webアプリケーションの構築にはセッションが状態とログイン情報の維持に重要です。

テンプレーティング

テンプレートはUIを持つWebアプリケーションにとって重要です。Flightと使用できるいくつかのテンプレートエンジンがあります。

貢献

共有したいプラグインがありますか?リストに追加するためにプルリクエストを送信してください!

Awesome-plugins/ghost_session

Ghostff/Session

PHP セッションマネージャー(非ブロッキング、フラッシュ、セグメント、セッション暗号化)。PHP open_ssl を使用してセッション データのオプションの暗号化/復号化をサポートします。File, MySQL, Redis, and Memcached をサポートします。

こちらをクリックしてコードを表示します。

インストール

Composer でインストールします。

composer require ghostff/session

基本的な構成

デフォルトの設定を使用するには何も渡す必要はありません。詳細な設定については、Github Readmeを参照してください。

use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

// 各ページの読み込みでセッションをコミットする必要がありますことを覚えておいてください
// または、構成で auto_commit を実行する必要があります。

簡単な例

これがこの使用方法の簡単な例です。

Flight::route('POST /login', function() {
    $session = Flight::session();

    // ここでログインのロジックを実行します
    // パスワードを検証するなど。

    // ログインに成功した場合
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // セッションに書き込んだら、意図的にコミットする必要があります。
    $session->commit();
});

// このチェックは制限されたページのロジックで実行するか、ミドルウェアでラップできます。
Flight::route('/some-restricted-page', function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }

    // ここで制限されたページのロジックを実行します
});

// ミドルウェア版
Flight::route('/some-restricted-page', function() {
    // 通常のページロジック
})->addMiddleware(function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }
});

より複雑な例

これがこの使用方法のより複雑な例です。

use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

// 最初の引数としてセッション構成ファイルのカスタムパスを設定します
// または、カスタム配列を与えます
$app->register('session', Session::class, [ 
    [
        // セッション データをデータベースに保存したい場合(例: 「すべてのデバイスからログアウト」機能)
        Session::CONFIG_DRIVER        => Ghostff\Session\Drivers\MySql::class,
        Session::CONFIG_ENCRYPT_DATA  => true,
        Session::CONFIG_SALT_KEY      => hash('sha256', 'my-super-S3CR3T-salt'), // これは別のものに変更してください
        Session::CONFIG_AUTO_COMMIT   => true, // これは必要で、commit() が難しい場合のみ実行してください。
                                                // さらに、Flight::after('start', function() { Flight::session()->commit(); }); を実行できます。
        Session::CONFIG_MYSQL_DS         => [
            'driver'    => 'mysql',             # Database driver for PDO dns eg(mysql:host=...;dbname=...)
            'host'      => '127.0.0.1',         # Database host
            'db_name'   => 'my_app_database',   # Database name
            'db_table'  => 'sessions',          # Database table
            'db_user'   => 'root',              # Database username
            'db_pass'   => '',                  # Database password
            'persistent_conn'=> false,          # スクリプトがデータベースにアクセスするたびに新しい接続を確立するオーバーヘッドを避ける。詳細は自分で確認してください
        ]
    ] 
]);

助けて! 私のセッションデータが持続しません!

セッションデータを設定してもリクエスト間で持続しない場合、セッションデータをコミットすることを忘れている可能性があります。$session->commit() を呼び出して設定した後でこれを実行してください。

Flight::route('POST /login', function() {
    $session = Flight::session();

    // ここでログインのロジックを実行します
    // パスワードを検証するなど。

    // ログインに成功した場合
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // セッションに書き込んだら、意図的にコミットする必要があります。
    $session->commit();
});

これを回避する方法として、セッション サービスを設定するときに構成で auto_committrue に設定します。これにより、各リクエスト後にセッションデータが自動的にコミットされます。

$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 スクリプト

スクリプトは、3 つのセットのスクリプトに分かれています。

スクリプトディレクトリは次のとおりです:

 <root dir>
     |
     +-- base.sql
     |
     +-- /migrations
              |
              +-- /up
                   |
                   +-- 00001.sql
                   +-- 00002.sql
              +-- /down
                   |
                   +-- 00000.sql
                   +-- 00001.sql

マルチ開発環境

複数の開発者や複数のブランチで作業する場合、次の番号を特定するのは難しいです。

その場合、バージョン番号の後にサフィックス "-dev" を付けます。

シナリオを見てみましょう:

どちらの場合も、開発者は 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 を使用してプロジェクトに統合する

基本的な使用法は

例えばを見る:

<?php
// 接続 URI を作成
// 詳細: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');

// データベースまたはデータベースをその URI で処理するように登録します:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);

// マイグレーションインスタンスを作成する
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');

// 実行からの情報を受け取るためにコールバック進捗関数を追加します
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
    echo "$action, $currentVersion, ${fileInfo['description']}\n";
});

// "base.sql" スクリプトを使用してデータベースを復元し
// データベースのバージョンを最新バージョンまでアップグレードするためのすべてのスクリプトを実行します
$migration->reset();

// 現在のバージョンから $version 番号までのデータベースのバージョンのためのすべてのスクリプトを実行します;
// バージョン番号が指定されていない場合、最後のデータベースバージョンまで移行します
$migration->update($version = null);

マイグレーションオブジェクトは、データベースのバージョンを制御します。

プロジェクト内のバージョン管理作成

<?php
// データベースまたはデータベースをその URI で処理するように登録します:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);

// マイグレーションインスタンスを作成する
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');

// このコマンドは、データベース内にバージョンテーブルを作成します
$migration->createVersion();

現在のバージョンの取得

<?php
$migration->getCurrentVersion();

コールバックを追加して進捗を制御

<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
    echo "コマンドを実行中: $command バージョン $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});

Db ドライバーインスタンスの取得

<?php
$migration->getDbDriver();

使用するには、次を訪れてください: https://github.com/byjg/anydataset-db

部分的なマイグレーションを避ける(MySQL では利用できません)

部分的なマイグレーションは、エラーや手動の中断によりマイグレーションスクリプトがプロセスの途中で中断される場合です。

マイグレーションテーブルは partial up または partial down の状態になり、再度移行できるようになる前に手動で修正する必要があります。

この状況を避けるために、マイグレーションがトランザクショナルコンテキストで実行されることを指定できます。 マイグレーションスクリプトが失敗すると、トランザクションはロールバックされ、マイグレーションテーブルは complete とマークされ、 バージョンはエラーを引き起こしたスクリプトの直前のバージョンになります。

この機能を有効にするには、withTransactionEnabled メソッドを呼び出して、true をパラメータとして渡す必要があります:

<?php
$migration->withTransactionEnabled(true);

注: この機能は、MySQL では利用できません。DDL コマンドをトランザクション内でサポートしていないためです。 このメソッドを MySQL で使用した場合、マイグレーションは静かに無視します。 詳細情報: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html

Postgres 用の SQL マイグレーションを書く際のヒント

トリガーと SQL 関数の作成時

-- DO
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
    BEGIN
        -- empname と salary が指定されていることを確認します
        IF NEW.empname IS NULL THEN
            RAISE EXCEPTION 'empname cannot be null'; -- これらのコメントが空でも関係ありません
        END IF; --
        IF NEW.salary IS NULL THEN
            RAISE EXCEPTION '% cannot have null salary', NEW.empname; --
        END IF; --

        -- 誰が私たちのために働いているのか、彼らはそれのために支払わなければなりませんか?
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION '% cannot have a negative salary', NEW.empname; --
        END IF; --

        -- 誰が給与を変更したのかを記憶して
        NEW.last_date := current_timestamp; --
        NEW.last_user := current_user; --
        RETURN NEW; --
    END; --
$emp_stamp$ LANGUAGE plpgsql;

-- DON'T
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
    BEGIN
        -- empname と salary が指定されていることを確認します
        IF NEW.empname IS NULL THEN
            RAISE EXCEPTION 'empname cannot be null';
        END IF;
        IF NEW.salary IS NULL THEN
            RAISE EXCEPTION '% cannot have null salary', NEW.empname;
        END IF;

        -- 誰が私たちのために働いているのか、彼らはそれのために支払わなければなりませんか?
        IF NEW.salary < 0 THEN
            RAISE EXCEPTION '% cannot have a negative salary', NEW.empname;
        END IF;

        -- 誰が給与を変更したのかを記憶して
        NEW.last_date := current_timestamp;
        NEW.last_user := current_user;
        RETURN NEW;
    END;
$emp_stamp$ LANGUAGE plpgsql;

PDO データベース抽象層は SQL ステートメントのバッチを実行できないため、byjg/migration がマイグレーションファイルを読み込むと、ファイルの内容全体をセミコロンで分割し、ステートメントを個別に実行する必要があります。ただし、1 つの種類のステートメントはその本体の間に複数のセミコロンを持つことがあります: 関数です。

関数を正しく解析できるようにするために、byjg/migration 2.1.0 からマイグレーションファイルを セミコロン + EOL シーケンスで分割するようになりました。これにより、関数定義の各内部セミコロンの後に空のコメントを追加すると、byjg/migration がそれを解析できるようになります。

不幸にも、これらのコメントのいずれかを追加するのを忘れると、ライブラリは CREATE FUNCTION ステートメントを複数の部分に分割し、マイグレーションは失敗します。

コロン文字(:)を避ける

-- DO
CREATE TABLE bookings (
  booking_id UUID PRIMARY KEY,
  booked_at  TIMESTAMPTZ NOT NULL CHECK (CAST(booked_at AS DATE) <= check_in),
  check_in   DATE NOT NULL
);

-- DON'T
CREATE TABLE bookings (
  booking_id UUID PRIMARY KEY,
  booked_at  TIMESTAMPTZ NOT NULL CHECK (booked_at::DATE <= check_in),
  check_in   DATE NOT NULL
);

PDO は名前付きパラメータのプレフィックスとしてコロン文字を使用するため、他のコンテキストでの使用は問題を引き起こします。

例えば、PostgreSQL ステートメントは :: を使用して型の間で値をキャストできます。 一方で、PDO はこれを無効な名前付きパラメータとして解釈し、無効なコンテキストで失敗します。

この不一致を修正する唯一の方法は、コロンを完全に避けることです(この場合、PostgreSQL にも代替構文があります: CAST(value AS type))。

SQL エディタを使用する

最後に、手動で SQL マイグレーションを書くことは面倒ですが、SQL 構文を理解し、オートコンプリートを提供し、現在のデータベーススキーマを調査しているエディタを使用すれば、格段に簡単になります。

1 つのスキーマ内での異なるマイグレーションの処理

同じスキーマ内で異なるマイグレーションスクリプトやバージョンを作成する必要がある場合、可能ですがリスクが高く、私は 全く推奨しません

これを行うには、コンストラクタにパラメータを渡して異なる "マイグレーションテーブル" を作成する必要があります。

<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "NEW_MIGRATION_TABLE_NAME");

セキュリティ上の理由から、この機能はコマンドラインでは利用できませんが、環境変数 MIGRATION_VERSION を使用して名前を保存できます。

この機能を使用しないことを強く推奨します。推奨は、1 つのスキーマにつき 1 つのマイグレーションです。

ユニットテストの実行

基本的なユニットテストは次のように実行できます:

vendor/bin/phpunit

データベーステストの実行

統合テストを実行するには、データベースを立ち上げておく必要があります。基本的な docker-compose.yml を提供しており、テストのためにデータベースを起動する際に使用できます。

データベースの実行

docker-compose up -d postgres mysql mssql

テストを実行

vendor/bin/phpunit
vendor/bin/phpunit tests/SqliteDatabase*
vendor/bin/phpunit tests/MysqlDatabase*
vendor/bin/phpunit tests/PostgresDatabase*
vendor/bin/phpunit tests/SqlServerDblibDatabase*
vendor/bin/phpunit tests/SqlServerSqlsrvDatabase*

オプションとして、ユニットテストで使用されるホストとパスワードを設定できます。

export MYSQL_TEST_HOST=localhost     # デフォルトは localhost
export MYSQL_PASSWORD=newpassword    # null パスワードが必要な場合は '.' を使用
export PSQL_TEST_HOST=localhost      # デフォルトは localhost
export PSQL_PASSWORD=newpassword     # null パスワードが必要な場合は '.' を使用
export MSSQL_TEST_HOST=localhost     # デフォルトは localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db      # デフォルトは /tmp/test.db

Awesome-plugins/session

FlightPHP セッション - 軽量なファイルベースのセッション ハンドラ

これは、Flight PHP Framework 向けの軽量でファイルベースのセッション ハンドラ プラグインです。ノンブロッキングのセッション読み込み、オプションの暗号化、オートコミット機能、開発用のテスト モードなどの機能を提供し、セッション管理を簡単かつ強力にします。セッション データはファイルに保存されるため、データベースを必要としないアプリケーションに理想的です。

データベースを使用したい場合は、同じ機能の多くを持つがデータベース バックエンドを備えた ghostff/session プラグインを参照してください。

完全なソース コードと詳細については、Github リポジトリを訪問してください。

インストール

Composer を介してプラグインをインストールします:

composer require flightphp/session

基本的な使用方法

Flight アプリケーションで flightphp/session プラグインを使用する簡単な例です:

require 'vendor/autoload.php';

use flight\Session;

$app = Flight::app();

// セッション サービスを登録
$app->register('session', Session::class);

// セッションを使用した例のルート
Flight::route('/login', function() {
    $session = Flight::session();
    $session->set('user_id', 123);
    $session->set('username', 'johndoe');
    $session->set('is_admin', false);

    echo $session->get('username'); // 出力: johndoe
    echo $session->get('preferences', 'default_theme'); // 出力: default_theme

    if ($session->get('user_id')) {
        Flight::json(['message' => 'User is logged in!', 'user_id' => $session->get('user_id')]);
    }
});

Flight::route('/logout', function() {
    $session = Flight::session();
    $session->clear(); // すべてのセッション データをクリア
    Flight::json(['message' => 'Logged out successfully']);
});

Flight::start();

重要なポイント

構成

登録時にオプションの配列を渡すことで、セッション ハンドラをカスタマイズできます:

// はい、二重配列です :)
$app->register('session', Session::class, [ [
    'save_path' => '/custom/path/to/sessions',         // セッション ファイルのディレクトリ
    'prefix' => 'myapp_',                              // セッション ファイルのプレフィックス
    'encryption_key' => 'a-secure-32-byte-key-here',   // 暗号化を有効にする (AES-256-CBC のために 32 バイト推奨)
    'auto_commit' => false,                            // オートコミットを無効にして手動制御
    'start_session' => true,                           // 自動的にセッションを開始 (デフォルト: true)
    'test_mode' => false,                              // 開発用のテスト モードを有効
    'serialization' => 'json',                         // シリアル化方法: 'json' (デフォルト) または 'php' (レガシー)
] ]);

構成オプション

Option Description Default Value
save_path セッション ファイルが保存されるディレクトリ sys_get_temp_dir() . '/flight_sessions'
prefix 保存されたセッション ファイルのプレフィックス sess_
encryption_key AES-256-CBC 暗号化のためのキー (オプション) null (暗号化なし)
auto_commit シャットダウン時にセッション データを自動保存 true
start_session 自動的にセッションを開始 true
test_mode PHP セッションに影響を与えないテスト モードで実行 false
test_session_id テスト モード用のカスタム セッション ID (オプション) 設定されていない場合ランダム生成
serialization シリアル化方法: 'json' (デフォルト、安全) または 'php' (レガシー、オブジェクトを許可) 'json'

シリアル化モード

このライブラリはデフォルトで JSON シリアル化 を使用し、セッション データの安全性が高く、PHP オブジェクト注入の脆弱性を防ぎます。セッションに PHP オブジェクトを保存する必要がある場合 (ほとんどのアプリでは推奨されません) は、レガシーの PHP シリアル化を選択できます:

注: JSON シリアル化を使用している場合、オブジェクトを保存しようとすると例外が発生します。

高度な使用方法

手動コミット

オートコミットを無効にした場合、変更を手動でコミットする必要があります:

$app->register('session', Session::class, ['auto_commit' => false]);

Flight::route('/update', function() {
    $session = Flight::session();
    $session->set('key', 'value');
    $session->commit(); // 変更を明示的に保存
});

暗号化によるセッション セキュリティ

機密データを保護するために暗号化を有効にします:

$app->register('session', Session::class, [
    'encryption_key' => 'your-32-byte-secret-key-here'
]);

Flight::route('/secure', function() {
    $session = Flight::session();
    $session->set('credit_card', '4111-1111-1111-1111'); // 自動的に暗号化
    echo $session->get('credit_card'); // 取得時に復号化
});

セッション ID の再生成

セキュリティのために (例: ログイン後) セッション ID を再生成します:

Flight::route('/post-login', function() {
    $session = Flight::session();
    $session->regenerate(); // 新しい ID、データを保持
    // または
    $session->regenerate(true); // 新しい ID、古いデータを削除
});

ミドルウェアの例

セッション ベースの認証でルートを保護します:

Flight::route('/admin', function() {
    Flight::json(['message' => 'Welcome to the admin panel']);
})->addMiddleware(function() {
    $session = Flight::session();
    if (!$session->get('is_admin')) {
        Flight::halt(403, 'Access denied');
    }
});

これはミドルウェアでの簡単な例です。より詳細な例については、middleware ドキュメントを参照してください。

メソッド

Session クラスは以下のメソッドを提供します:

get()id() を除くすべてのメソッドは、チェイニングのために Session インスタンスを返します。

このプラグインを使う理由

技術詳細

貢献

貢献を歓迎します! リポジトリ をフォークし、変更を加えてプル リクエストを送信してください。バグの報告や機能の提案は Github のイシュー トラッカーで行ってください。

ライセンス

このプラグインは MIT ライセンスの下でライセンスされています。詳細は Github リポジトリ を参照してください。

Awesome-plugins/runway

ランウェイ

ランウェイはCLIアプリケーションで、Flightアプリケーションの管理を支援します。コントローラを生成したり、すべてのルートを表示したりすることができます。優れたadhocore/php-cliライブラリに基づいています。

こちらをクリックして、コードを表示してください。

インストール

Composerを使用してインストールしてください。

composer require flightphp/runway

基本設定

ランウェイを実行する最初の回は、セットアッププロセスを進め、プロジェクトのルートに.runway.json構成ファイルを作成します。このファイルには、ランウェイが正しく動作するために必要ないくつかの構成が含まれています。

使用法

ランウェイには、Flightアプリケーションを管理するために使用できる複数のコマンドがあります。ランウェイを使用する方法は2つあります。

  1. スケルトンプロジェクトを使用している場合、プロジェクトのルートから php runway [command] を実行できます。
  2. 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 テーブルがある場合:idnameemailcreated_atupdated_atapp/records/UserRecord.php ファイルに類似したファイルが作成されます:

<?php

declare(strict_types=1);

namespace app\records;

/**
 * ユーザーテーブルのアクティブレコードクラス。
 * @link https://docs.flightphp.com/awesome-plugins/active-record
 * 
 * @property int $id
 * @property string $name
 * @property string $email
 * @property string $created_at
 * @property string $updated_at
 * // 関係を定義した場合、ここに関係を追加できます
 * @property CompanyRecord $company 関係の例
 */
class UserRecord extends \flight\ActiveRecord
{
    /**
     * @var array $relations モデルの関係を設定します
     *   https://docs.flightphp.com/awesome-plugins/active-record#relationships
     */
    protected array $relations = [];

    /**
     * コンストラクタ
     * @param mixed $databaseConnection データベースへの接続
     */
    public function __construct($databaseConnection)
    {
        parent::__construct($databaseConnection, 'users');
    }
}

すべてのルートを表示する

登録されているすべてのFlightのルートを表示します。

php runway routes

特定のルートのみを表示したい場合、フラグを渡してルートをフィルタリングできます。

# GETルートのみを表示
php runway routes --get

# POSTルートのみを表示
php runway routes --post

# など

ランウェイのカスタマイズ

Flight向けのパッケージを作成しているか、プロジェクトに独自のカスタムコマンドを追加したい場合は、プロジェクト/パッケージ向けに src/commands/flight/commands/app/commands/、または commands/ ディレクトリを作成してください。

コマンドを作成するには、AbstractBaseCommandクラスを拡張し、__constructメソッドとexecuteメソッドを最低限実装します。

<?php

declare(strict_types=1);

namespace flight\commands;

class ExampleCommand extends AbstractBaseCommand
{
    /**
     * コンストラクタ
     *
     * @param array<string,mixed> $config .runway-config.jsonからのJSON構成
     */
    public function __construct(array $config)
    {
        parent::__construct('make:example', 'ドキュメントの例を作成', $config);
        $this->argument('<funny-gif>', '面白いGIFの名前');
    }

    /**
     * 関数を実行
     *
     * @return void
     */
    public function execute(string $controller)
    {
        $io = $this->app()->io();

        $io->info('例を作成します...');

        // ここで何かを実行

        $io->ok('例が作成されました!');
    }
}

独自のカスタムコマンドをFlightアプリケーションに組み込む方法については、adhocore/php-cliドキュメントを参照してください!

Awesome-plugins/tracy_extensions

Tracy Flight パネル拡張

これは、Flight をより豊かに扱うための拡張機能のセットです。

これはパネルです

Flight バー

各パネルは、アプリケーションに関する非常に役立つ情報を表示します!

Flight データ Flight データベース Flight リクエスト

こちら をクリックしてコードを表示します。

インストール

composer require flightphp/tracy-extensions --dev を実行すると、すぐに開始できます!

設定

これを開始するには、ほとんど設定は必要ありません。この拡張機能を使用する前に、Tracy デバッガーを開始する必要があります https://tracy.nette.org/en/guide:

<?php

use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;

// ブートストラップコード
require __DIR__ . '/vendor/autoload.php';

Debugger::enable();
// 環境を指定する必要がある場合、Debugger::enable(Debugger::DEVELOPMENT) を使用します

// アプリでデータベース接続を使用する場合、
// 開発環境でのみ使用する(本番環境では使用しないでください!)
// 通常のPDO接続と同じパラメータです
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// Flight フレームワークにこれを付与する場合
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// クエリを実行すると、時間、クエリ、パラメータをキャプチャします

// これでつながります
if(Debugger::$showBar === true) {
    // これをfalseにしないと、Tracy が正しく表示されません
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

// 追加のコード

Flight::start();

追加の設定

セッションデータ

カスタムのセッションハンドラ(例: ghostff/session)を使用している場合、Tracy にセッションデータの配列を渡すと、自動的に出力されます。TracyExtensionLoader コンストラクタの2番目のパラメータで session_data キーを使って渡します。


use Ghostff\Session\Session;
// または flight\Session を使用します;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

if(Debugger::$showBar === true) {
    // これをfalseにしないと、Tracy が正しく表示されません
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}

// ルートや他のもの...

Flight::start();

Latte

プロジェクトに Latte をインストールしている場合、Latte パネルを使用してテンプレートを分析できます。TracyExtensionLoader コンストラクタの2番目のパラメータで latte キーで Latte インスタンスを渡します。


use Latte\Engine;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('latte', Engine::class, [], function($latte) {
    $latte->setTempDirectory(__DIR__ . '/temp');

    // ここで Latte パネルを Tracy に追加します
    $latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
});

if(Debugger::$showBar === true) {
    // これをfalseにしないと、Tracy が正しく表示されません
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

Awesome-plugins/apm

FlightPHP APM ドキュメント

FlightPHP APM にようこそ—あなたのアプリのパーソナルなパフォーマンスコーチです! このガイドは、Application Performance Monitoring (APM) を FlightPHP とともに設定し、使用し、マスターするためのロードマップです。 遅いリクエストを追跡したり、レイテンシのチャートに熱中したりするかどうか、私たちがカバーしています。 アプリをより速くし、ユーザーをより幸せにし、デバッグセッションを簡単に行うようにしましょう!

APM の重要性

想像してみてください:あなたのアプリは忙しいレストランです。 注文にどれくらい時間がかかるかを追跡する方法がないと、キッチンがどこで遅れているのかを推測し、顧客が不機嫌になって去る理由を当てずっぽうに考えています。 APM はあなたの副料理長のようなものです—着信リクエストからデータベースクエリまで、すべてのステップを監視し、遅延を引き起こすものをフラグします。 遅いページはユーザーを失います(研究によると、サイトの読み込みに 3 秒以上かかると 53% が離脱する!)、APM はそれらの問題を 事前に キャッチします。 これは積極的な安心感です—「なぜこれが壊れているの?」という瞬間を少なくし、「これがどれほどスムーズに動くか!」という勝利を増やします。

インストール

Composer で始めましょう:

composer require flightphp/apm

必要なもの:

始め方

APM の素晴らしさをステップバイステップで紹介します:

1. APM を登録する

トラッキングを開始するために、index.php または services.php ファイルにこれを追加します:

use flight\apm\logger\LoggerFactory;
use flight\Apm;

$ApmLogger = LoggerFactory::create(__DIR__ . '/../../.runway-config.json');
$Apm = new Apm($ApmLogger);
$Apm->bindEventsToFlightInstance($app);

何が起こっているのか?

プロチップ: サンプリング アプリが忙しい場合、すべての リクエストをログするとオーバーロードする可能性があります。 サンプル率(0.0 から 1.0)を使用します:

$Apm = new Apm($ApmLogger, 0.1); // リクエストの 10% をログ

これでパフォーマンスを維持しつつ、しっかりしたデータが得られます。

2. 設定する

.runway-config.json を作成するためにこれを実行します:

php vendor/bin/runway apm:init

これは何をするのか?

このプロセスは、このセットアップのマイグレーションを実行するかどうかも尋ねます。 初めて設定する場合、答えは yes です。

なぜ二つの場所が必要なのか? 生のメトリクスは急速に蓄積します(フィルタリングされていないログを考えてください)。 ワーカーがそれを構造化された宛先で処理し、ダッシュボード用にします。 整理された状態を保ちます!

3. ワーカーでメトリクスを処理する

ワーカーは生のメトリクスをダッシュボード対応のデータに変換します。 一度実行します:

php vendor/bin/runway apm:worker

これは何をしているのか?

継続的に実行する ライブアプリの場合、継続的な処理が必要です。 オプションは次の通りです:

なぜこれが必要なのか? ワーカーなしではダッシュボードは空です。 これは生のログと実用的な洞察の橋渡しです。

4. ダッシュボードを起動する

アプリのバイタルサインを表示:

php vendor/bin/runway apm:dashboard

これは何?

カスタマイズ:

php vendor/bin/runway apm:dashboard --host 0.0.0.0 --port 8080 --php-path=/usr/local/bin/php

ブラウザで URL を開いて探検しましょう!

プロダクションモード

プロダクションでは、ファイアウォールや他のセキュリティ対策があるため、ダッシュボードを実行するためにいくつかの手法を試す必要があるかもしれません。 オプションは次の通りです:

違うダッシュボードが欲しい?

独自のダッシュボードを作成できます! データの表示方法のアイデアのために vendor/flightphp/apm/src/apm/presenter ディレクトリを見てください!

ダッシュボードの機能

ダッシュボードはあなたの APM 本部です—ここに表示されるものを紹介します:

追加機能:

: /users へのリクエストは次のように表示される可能性があります:

カスタムイベントの追加

API 呼び出しや支払いプロセスなどのものを追跡:

use flight\apm\CustomEvent;

$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('api_call', [
    'endpoint' => 'https://api.example.com/users',
    'response_time' => 0.25,
    'status' => 200
]));

どこに表示されるのか? ダッシュボードのリクエスト詳細の下の「カスタムイベント」—展開可能できれいな JSON フォーマット。

使用例:

$start = microtime(true);
$apiResponse = file_get_contents('https://api.example.com/data');
$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('external_api', [
    'url' => 'https://api.example.com/data',
    'time' => microtime(true) - $start,
    'success' => $apiResponse !== false
]));

これでその API がアプリを遅らせているかどうかがわかります!

データベース監視

PDO クエリをこのように追跡:

use flight\database\PdoWrapper;

$pdo = new PdoWrapper('sqlite:/path/to/db.sqlite');
$Apm->addPdoConnection($pdo);

得られるもの:

注意点:

例の出力:

ワーカーのオプション

ワーカーを好みに合わせて調整:

:

php vendor/bin/runway apm:worker --daemon --batch_size 100 --timeout 3600

1 時間実行、一度に 100 件のメトリクスを処理。

アプリ内のリクエスト ID

各リクエストには一意のリクエスト ID があり、追跡に使用できます。 ログとメトリクスを関連付けるために、エラーページにリクエスト ID を追加できます:

Flight::map('error', function($message) {
    // Get the request ID from the response header X-Flight-Request-Id
    $requestId = Flight::response()->getHeader('X-Flight-Request-Id');

    // Additionally you could fetch it from the Flight variable
    // This method won't work well in swoole or other async platforms.
    // $requestId = Flight::get('apm.request_id');

    echo "Error: $message (Request ID: $requestId)";
});

アップグレード

APM の新しいバージョンにアップグレードする場合、データベースのマイグレーションが必要になる可能性があります。 次のコマンドを実行して:

php vendor/bin/runway apm:migrate

これはデータベーススキーマを最新バージョンに更新するための必要なマイグレーションを実行します。

注意: APM データベースが大きい場合、これらのマイグレーションには時間がかかる可能性があります。 オフピーク時に実行することを検討してください。

古いデータの消去

データベースを整理するために、古いデータを消去できます。 これは忙しいアプリを実行していて、データベースのサイズを管理したい場合に特に便利です。 次のコマンドを実行して:

php vendor/bin/runway apm:purge

これはデータベースから 30 日より古いすべてのデータを削除します。 --days オプションで日数を調整できます:

php vendor/bin/runway apm:purge --days 7

これはデータベースから 7 日より古いすべてのデータを削除します。

トラブルシューティング

困った? これを試してください:

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);
}

便利なヒント

コードのデバッグ中に、データを出力するための非常に役立つ関数がいくつかあります。

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">
            &copy; 著作権
        </div>
    </body>
</html>

そして、コンテンツブロック内でレンダリングされるファイルがあります:

<!-- app/views/home.latte -->
<!-- このファイルが layout.latte ファイル内にあることを Latte に伝えます -->
{extends layout.latte}

<!-- レイアウト内のコンテンツブロック内に表示されるコンテンツです -->
{block content}
    <h1>ホームページ</h1>
    <p>アプリへようこそ!</p>
{/block}

次に、この内容を関数またはコントローラ内でレンダリングする際には、次のように行います:

// シンプルなルート
Flight::route('/', function () {
    Flight::latte()->render('home.latte', [
        'title' => 'ホームページ'
    ]);
});

// もしくはコントローラを使用している場合
Flight::route('/', [HomeController::class, 'index']);

// HomeController.php
class HomeController
{
    public function index()
    {
        Flight::latte()->render('home.latte', [
            'title' => 'ホームページ'
        ]);
    }
}

ラッテを最大限に活用するための詳細については、Latte Documentation を参照してください。

Awesome-plugins/awesome_plugins

素晴らしいプラグイン

Flight は非常に拡張性が高いです。Flight アプリケーションに機能を追加するためのいくつかのプラグインがあります。一部は Flight チームによって公式にサポートされており、他のものは Flight を始めるためのマイクロ/ライトライブラリです。

API ドキュメンテーション

API ドキュメンテーションは、どの API にとっても重要です。開発者が API とどのように相互作用するかを理解し、何を期待するかを知るのに役立ちます。Flight プロジェクトの API ドキュメンテーションを生成するためのいくつかのツールがあります。

アプリケーションのパフォーマンス監視 (APM)

アプリケーションのパフォーマンス監視 (APM) は、どのアプリケーションにとっても重要です。アプリケーションがどのように動作しているかを理解し、ボトルネックがどこにあるかを知るのに役立ちます。Flight と一緒に使用できるいくつかの APM ツールがあります。

認証/認可

認証と認可は、誰が何にアクセスできるかを制御する必要があるアプリケーションにとって重要です。

キャッシング

キャッシングは、アプリケーションの速度を向上させる優れた方法です。Flight と一緒に使用できるいくつかのキャッシングライブラリがあります。

CLI

CLI アプリケーションは、アプリケーションと相互作用するための優れた方法です。コントローラーを生成したり、すべてのルートを表示したり、その他さまざまな用途に使用できます。

クッキー

クッキーは、クライアント側に小さなデータを保存するための優れた方法です。ユーザーの好み、アプリケーションの設定などを保存するのに使用できます。

デバッグ

デバッグは、ローカル環境で開発する際に重要です。デバッグ体験を向上させるためのいくつかのプラグインがあります。

データベース

データベースは、ほとんどのアプリケーションの基盤です。これを使ってデータを保存し、取得します。一部はクエリを書くためのシンプルなラッパーで、一部はフル機能の ORM です。

暗号化

暗号化は、機密データを保存するアプリケーションにとって重要です。データを暗号化し、復号化するのはそれほど難しくありませんが、暗号化キーを適切に保存すること 難しい場合があります。一番重要なのは、暗号化キーを公開ディレクトリに保存したり、コードリポジトリにコミットしたりしないことです。

ジョブキュー

ジョブキューは、タスクを非同期で処理するための非常に役立つものです。これには、メールの送信、画像の処理、リアルタイムで処理する必要がないものは何でも使用できます。

セッション

セッションは API にはあまり役立ちませんが、Web アプリケーションを構築する際には、状態の維持やログイン情報の管理に重要です。

テンプレート

テンプレートは、UI を持つ Web アプリケーションの基盤です。Flight と一緒に使用できるいくつかのテンプレートエンジンがあります。

貢献

共有したいプラグインがありますか?リストに追加するためのプルリクエストを提出してください!

Media

メディア

私たちは、Flightに関するインターネット上のさまざまなメディアの種類を追跡しようとしました。Flightについてもっと学ぶために使用できるさまざまなリソースについては、以下をご覧ください。

記事とレビュー

ビデオとチュートリアル

Examples

迅速なスタートが必要ですか?

新しいFlightプロジェクトを始めるためのオプションは2つあります:

コミュニティが提供した例:

インスピレーションが必要ですか?

これらはFlightチームによって公式にスポンサーされているわけではありませんが、Flightを使って構築された自分のプロジェクトを構成する方法についてのアイデアを得ることができます!

自分の例を共有したいですか?

共有したいプロジェクトがある場合は、このリストに追加するためのプルリクエストを送信してください!

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を使用して機能的なブログアプリケーションを作成する方法を学びます。このガイドでは以下の内容を説明します:

このチュートリアルは、すべての要素がどのようにリアルなアプリケーションに組み合わさるのかを見ることを望む初心者に最適です。

非公式ガイド

これらのガイドはFlightチームによって公式に維持されているわけではありませんが、コミュニティによって作成された貴重なリソースです。さまざまなトピックとユースケースをカバーし、Flight PHPの使用に関する追加の洞察を提供します。

Flight Frameworkを使用したRESTful APIの作成

このガイドでは、Flight PHPフレームワークを使用してRESTful APIを作成する方法を説明します。APIの設定、ルートの定義、JSONレスポンスを返す基本についてカバーします。

シンプルなブログの構築

このガイドでは、Flight PHPフレームワークを使用して基本的なブログを作成する方法を説明します。実際には2部構成で、基本についてのガイドと、プロダクションに対応したブログのためのより高度なトピックと改良についてのガイドがあります。

PHPでのポケモンAPIの構築: 初心者向けガイド

この楽しいガイドでは、Flight PHPを使用してシンプルなポケモンAPIを作成する方法を説明します。APIの設定、ルートの定義、JSONレスポンスを返す基本についてカバーします。

貢献

ガイドのアイデアがありますか?間違いを見つけましたか?貢献を歓迎します!私たちのガイドは、FlightPHPドキュメントリポジトリで維持されています。

Flightを使用して興味深いものを作成し、それをガイドとして共有したい場合は、プルリクエストを提出してください。知識を共有することで、Flightコミュニティの成長を助けます。

APIドキュメントをお探しですか?

Flightのコア機能やメソッドについての具体的な情報をお探しなら、私たちのドキュメントの学習セクションをチェックしてください。