Learn

フライトについて学ぶ

フライトは、PHPのための高速でシンプルで拡張性のあるフレームワークです。非常に汎用性があり、あらゆる種類のWebアプリケーションの構築に使用できます。 シンプルさを重視して構築されており、理解しやすく使用しやすい方法で書かれています。

重要なフレームワークのコンセプト

なぜフレームワークを使用するのか?

フレームワークを使用するメリットについて簡潔な記事がこちらにあります。フレームワークを使用する前に、その利点を理解することが重要です。

さらに、@lubianaによって優れたチュートリアルが作成されました。Flightに特化しているわけではありませんが、 このガイドはフレームワークを取り巻く主要なコンセプトとその使用上の利点を理解するのに役立ちます。 チュートリアルはこちらで見つけることができます。

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

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

コアトピック

オートローディング

アプリケーション内で自分自身のクラスをオートロードする方法を学びます。

ルーティング

Webアプリケーションのためのルートを管理する方法を学びます。これにはルートのグルーピング、ルートパラメータ、ミドルウェアも含まれます。

ミドルウェア

リクエストとレスポンスをフィルタリングするためにミドルウェアを使用する方法を学びます。

リクエスト

アプリケーション内でリクエストとレスポンスを処理する方法を学びます。

レスポンス

ユーザーにレスポンスを送信する方法を学びます。

HTMLテンプレート

組み込みのビューエンジンを使用してHTMLテンプレートをレンダリングする方法を学びます。

セキュリティ

一般的なセキュリティ脅威からアプリケーションを保護する方法を学びます。

設定

アプリケーションにフレームワークを設定する方法を学びます。

フライトの拡張

独自のメソッドとクラスを追加してフレームワークを拡張する方法を学びます。

イベントとフィルタリング

イベントシステムを使用してメソッドと内部フレームワークメソッドにフックを追加する方法を学びます。

依存性注入コンテナ

依存性注入コンテナ(DIC)を使用してアプリケーションの依存関係を管理する方法を学びます。

フレームワークAPI

フレームワークのコアメソッドについて学びます。

v3への移行

後方互換性は大部分が維持されていますが、v2からv3への移行時に認識しておくべき変更がいくつかあります。

トラブルシューティング

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

Learn/stopping

停止

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

Flight::halt();

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

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

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

Flight::stop();

Learn/errorhandling

エラー処理

エラーと例外

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

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

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

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

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

見つかりません

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

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

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

Learn/flight_vs_laravel

フライト vs ララベル

ララベルとは何ですか?

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

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

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

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

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/security

セキュリティ

セキュリティはWebアプリケーションに関連する重要な要素です。アプリケーションが安全であり、ユーザーのデータが保護されていることを確認したいですね。Flight はウェブアプリケーションをセキュアにするための機能をいくつか提供しています。

ヘッダー

HTTPヘッダーはウェブアプリケーションをセキュアにする最も簡単な方法の一つです。ヘッダーを使用してクリックジャッキング、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');

// リファラーポリシー ヘッダーを設定して送信するリファラー情報の量を制御する
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');

// HTTPS を強制するために Strict-Transport-Security ヘッダーを設定する
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');

// 使用可能な機能やAPIを制御するために Permissions-Policy ヘッダーを設定する
Flight::response()->header('Permissions-Policy', 'geolocation=()');

これらは bootstrap.php または index.php ファイルの先頭に追加することができます。

フィルターとして追加

以下のように、フィルター/フックにこれらを追加することもできます。

// フィルター内でヘッダーを追加
Flight::before('start', function() {
    Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
    Flight::response()->header("Content-Security-Policy", "default-src 'self'");
    Flight::response()->header('X-XSS-Protection', '1; mode=block');
    Flight::response()->header('X-Content-Type-Options', 'nosniff');
    Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
    Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
    Flight::response()->header('Permissions-Policy', 'geolocation=()');
});

ミドルウェアとして追加

ミドルウェアクラスとしても追加できます。これはコードを清潔で整理された状態に保つのに適した方法です。

// app/middleware/SecurityHeadersMiddleware.php

namespace app\middleware;

class SecurityHeadersMiddleware
{
    public function before(array $params): void
    {
        Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
        Flight::response()->header("Content-Security-Policy", "default-src 'self'");
        Flight::response()->header('X-XSS-Protection', '1; mode=block');
        Flight::response()->header('X-Content-Type-Options', 'nosniff');
        Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
        Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
        Flight::response()->header('Permissions-Policy', 'geolocation=()');
    }
}

// index.php またはルートがある場所
// この空のグループは、すべてのルートに対するグローバルミドルウェアとして機能します。もちろん、同じことをして、特定のルートにのみこれを追加することもできます。
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 を使用する

カスタム関数を設定して、Latteテンプレート内でCSRFトークンを出力することもできます。

// CSRFトークンを出力するカスタム関数を設定
// 注: ビューはビューエンジンとして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リクエストかどうかを確認し、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, 'Invalid CSRF token');
            // または JSON応答用
            Flight::jsonHalt(['error' => 'Invalid CSRF token'], 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, 'Invalid CSRF token');
            }
        }
    }
}

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

結論

セキュリティは重要な要素であり、Webアプリケーションが安全であることを確認することが重要です。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

ルーティング

Note: ルーティングについてさらに理解したいですか? より詳しい説明については、"なぜフレームワークを使うのか?" ページを参照してください。

Flight における基本的なルーティングは、URL パターンとコールバック関数またはクラスとメソッドの配列との一致によって行われます。

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

ルートは定義された順番で一致します。リクエストに一致する最初のルートが実行されます。

コールバック/関数

コールバックは呼び出し可能な任意のオブジェクトを使用できます。よって、通常の関数を使用できます:

function hello() {
    echo 'hello world!';
}

Flight::route('/', 'hello');

クラス

クラスの静的メソッドを使用することもできます:

class Greeting {
    public static function hello() {
        echo 'hello world!';
    }
}

Flight::route('/', [ 'Greeting','hello' ]);

また、最初にオブジェクトを作成してからメソッドを呼び出すこともできます:


// Greeting.php
class Greeting
{
    public function __construct() {
        $this->name = 'John Doe';
    }

    public function hello() {
        echo "Hello, {$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(Dependency Injection Container)を介した依存性の注入

コンテナ(PSR-11、PHP-DI、Dice など)を使った依存性の注入を行いたい場合、 依存性の注入を使用できる唯一のタイプのルートは、オブジェクトを直接作成して自分自身でオブジェクトを作成するコンテナを使用するか、クラスとメソッドを呼び出すために文字列を使用するルートです。詳細についてはDependency Injection ページを参照してください。

以下に簡単な例を示します:


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 の詳細については、Dependency Injection ページを参照してください
$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 'hello world!';
});

// GET リクエスト
$router->get('/users', function() {
    echo 'users';
});
// $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 には一致しません
});

Note: 名前付きパラメータで正規表現のグループ () に一致させることはサポートされていません。 :'(

オプションのパラメータ

一致させるためにオプションの名前付きパラメータを指定できます。一致しないオプションのパラメータは NULL として渡されます。

Flight::route(
  '/blog(/@year(/@month(/@day)))',
  function(?string $year, ?string $month, ?string $day) {
    // 以下の URL に一致します:
    // /blog/2012/12/10
    // /blog/2012/12
    // /blog/2012
    // /blog
  }
);

ワイルドカード

一致は個々の 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 'user:'.$id; }, false, 'user_view');

// あとでどこかのコードで
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5' を返します

あなたの URL が変更された場合、ルートエイリアスを使用すると、エイリアスを参照している場所全てを変更する必要がありません。 上記の例のように、ユーザーが /admin/users/@id に移動した場合、エイリアスを使用している場所全てを変更する必要はないため、エイリアスが非常に役立ちます。

ルートのエイリアスはグループ内でも機能します:

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
});

// あとでどこかのコードで
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5' を返します

ルート情報

一致するルート情報を検査したい場合は、ルートメソッドの第三引数として 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 に一致
  });
});

ストリーミング

streamWithHeaders() メソッドを使用して、クライアントに対して応答をストリーミングで送信することができます。 これは大きなファイル、長時間実行されるプロセス、または大規模な応答を生成する場合に役立ちます。 ルートをストリーミングする場合、通常のルートよりもやや異なる方法で処理されます。

注意: flight.v2.output_buffering が false に設定されている場合のみ、ストリーミング応答を使用できます。

マニュアルヘッダー付きストリーミング

ルートで stream() メソッドを使用することで、クライアントに対して応答をストリーミングすることができます。これを行う場合は、クライアントに何かを出力する前に全てのメソッドを手動で設定する必要があります。 これは header() php 関数または Flight::response()->setRealHeader() メソッドで行います。

Flight::route('/@filename', function($filename) {

    // パスをサニタイズする必要があります
    $fileNameSafe = basename($filename);

    // ルートの実行後にここに追加する追加ヘッダがある場合
    // 何かをエコーする前にそれ```md
# ルーティング

> **注意:** ルーティングについてさらに理解したいですか? より詳しい説明については、["なぜフレームワークを使うのか?"](learn/why-frameworks) ページを参照してください。

Flight における基本的なルーティングは、URL パターンとコールバック関数またはクラスとメソッドの配列との一致によって行われます。

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

ルートは定義された順番で一致します。リクエストに一致する最初のルートが実行されます。

コールバック/関数

コールバックは呼び出し可能な任意のオブジェクトを使用できます。よって、通常の関数を使用できます:

function hello() {
    echo 'hello world!';
}

Flight::route('/', 'hello');

クラス

クラスの静的メソッドを使用することもできます:

class Greeting {
    public static function hello() {
        echo 'hello world!';
    }
}

Flight::route('/', [ 'Greeting','hello' ]);

また、最初にオブジェクトを作成してからメソッドを呼び出すこともできます:


// Greeting.php
class Greeting
{
    public function __construct() {
        $this->name = 'John Doe';
    }

    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(Dependency Injection Container)を介した依存性の注入

コンテナ(PSR-11、PHP-DI、Dice など)を使った依存性の注入を行いたい場合、 依存性の注入を使用できる唯一のタイプのルートは、オブジェクトを直接作成して自分自身でオブジェクトを作成するコンテナを使用するか、クラスとメソッドを呼び出すために文字列を使用するルートです。詳細についてはDependency Injection ページを参照してください。

以下に簡単な例を示します:


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 の詳細については、Dependency Injection ページを参照してください
$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 'hello world!';
});

// GET リクエスト
$router->get('/users', function() {
    echo 'users';
});
// $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 には一致しません
});

注意: 名前付きパラメータで正規表現のグループ () に一致させることはサポートされていません。 :'(

オプションのパラメータ

一致させるためにオプションの名前付きパラメータを指定できます。一致しないオプションのパラメータは NULL として渡されます。

Flight::route(
  '/blog(/@year(/@month(/@day)))',
  function(?string $year, ?string $month, ?string $day) {
    // 以下の URL に一致します:
    // /blog/2012/12/10
    // /blog/2012/12
    // /blog/2012
    // /blog
  }
);

ワイルドカード

一致は個々の 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 'user:'.$id; }, false, 'user_view');

// あとでどこかのコードで
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5' を返します

あなたの URL が変更された場合、ルートエイリアスを使用すると、エイリアスを参照している場所全てを変更する必要がありません。 上記の例のように、ユーザーが /admin/users/@id に移動した場合、エイリアスを使用している場所全てを変更する必要はないため、エイリアスが非常に役立ちます。

ルートエイリアスはグループ内でも機能します:

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
});

// あとでどこかのコードで
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5' を返します

ルート情報

一致するルート情報を検査したい場合は、ルートメソッドの第三引数として 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 に一致
  });
});

ストリーミング

streamWithHeaders() メソッドを使用して、クライアントに対して応答をストリーミングで送信することができます。 これは大きなファイル、長時間実行されるプロセス、または大規模な応答を生成する場合に役立ちます。 ルートをストリーミングする場合、通常のルートよりもやや異なる方法で処理されます。

注意: flight.v2.output_buffering が false に設定されている場合のみ、ストリーミング応答を使用できます。

マニュアルヘッダー付きストリーミング

ルートで stream() メソッドを使用することで、クライアントに対して応答をストリーミングすることができます。これを行う場合は、クライアントに何かを出力する前に全てのメソッドを手動で設定する必要があります。 これは header() php 関数または Flight::response()->setRealHeader() メソッドで行います。

Flight::route('/@filename', function($filename) {

    // パスをサニタイズする必要があります
    $fileNameSafe = basename($filename);

    // ルートの実行後にここに追加する追加ヘッダがある場合
    // 何かをエコーする必要があります。

   ファイルのデータを取得します
   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 は使いやすく理解しやすいように設計されています。以下はフレームワークの完全なメソッドセットです。
これには、通常の静的メソッドであるコアメソッドと、フィルタリングやオーバーライドが可能なマップされたメソッドである拡張可能メソッドが含まれています。

## コアメソッド

これらのメソッドはフレームワークのコアであり、オーバーライドすることはできません。

```php
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 レスポンスを送信し、フレームワークを停止します。

mapregister で追加されたカスタムメソッドは、フィルタリングすることもできます。 これらのメソッドをどのようにマップするかの例については、Extending 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()を使用しています。これは、echoprintを使用してユーザーにレスポンスを送信し、Flightがそれをキャプチャして適切なヘッダーとともにユーザーに送り返すことができることを意味します。


// これは、「こんにちは、世界!」をユーザーのブラウザに送信します
Flight::route('/', function() {
    echo "こんにちは、世界!";
});

// HTTP/1.1 200 OK
// Content-Type: text/html
//
// こんにちは、世界!

代わりに、write()メソッドを呼び出して本文に追加することもできます。


// これは、「こんにちは、世界!」をユーザーのブラウザに送信します
Flight::route('/', function() {
    // 煩雑ですが、必要な場合には仕事を行います
    Flight::response()->write("こんにちは、世界!");

    // この時点で設定した本文を取得したい場合
    // 以下のようにすることができます
    $body = Flight::response()->getBody();
});

ステータスコード

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

Flight::route('/@id', function($id) {
    if($id == 123) {
        Flight::response()->status(200);
        echo "こんにちは、世界!";
    } else {
        Flight::response()->status(403);
        echo "禁止されています";
    }
});

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

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

レスポンスボディの設定

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

Flight::route('/', function() {
    Flight::response()->write("こんにちは、世界!");
});

// 以下と同じ

Flight::route('/', function() {
    echo "こんにちは、世界!";
});

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

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

Flight::route('/', function() {
    if($someCondition) {
        Flight::response()->write("こんにちは、世界!");
    } 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); };、または関数名 'minify' を受け入れることができます(たとえば、HTMLコードを縮小化する関数がある場合など)。

注意: 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() {
        // ここでresponse()オブジェクトに対してコールバックを適用します
        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メソッドを使用して、レスポンスのコンテンツタイプなどのヘッダーを設定できます。


// これは「こんにちは、世界!」をユーザーのブラウザにプレーンテキストとして送信します
Flight::route('/', function() {
    Flight::response()->header('Content-Type', 'text/plain');
    // または
    Flight::response()->setHeader('Content-Type', 'text/plain');
    echo "こんにちは、世界!";
});

JSON

Flightは、JSONおよびJSONP応答の送信をサポートしています。 JSON応答を送信するには、JSONにエンコードするデータを渡します。

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

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

2番目の引数としてステータスコードを渡すこともできます。

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(「他を参照してください」)ステータスコードを送信します。カスタムコードを設定することもできます。

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

停止

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

Flight::halt();

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

Flight::halt(200, 'ただいまメンテナンス中...');

haltを呼び出すことで、現在までの任意のレスポンス内容が破棄されます。フレームワークを停止して、現在のレスポンスを出力する場合は、stopメソッドを使用します。

Flight::stop();

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

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

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

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

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

// これにより、response()オブジェクトに設定された任意のヘッダーが保持されます。
Flight::response()->clearBody();

HTTPキャッシュ

Flightには、HTTPレベルのキャッシュを簡単に行うための組込みのサポートがあります。 キャッシュ条件が満たされると、FlightはHTTP304 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メソッドを使用して、ページが最終に変更された日付と時刻を設定できます。 クライアントは、最終変更値が変更されるまでキャッシュを維持します。

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応答を送信して処理を停止します。

ファイルのダウンロード

ファイルをダウンロードするためのヘルパーメソッドがあります。 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/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 はデフォルトでいくつかの基本的なテンプレート機能を提供します。

より複雑なテンプレートが必要な場合は、カスタムビュー セクションの Smarty と Latte の例を参照してください。

デフォルトのビューエンジン

ビューテンプレートを表示するには、テンプレートファイルの名前とオプションのテンプレートデータを使用して 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' => 'ホームページ']);

テンプレートファイルが次のようになっている場合:

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>ホームページ</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div>World</div>
  </body>
</html>

カスタムビューエンジン

Flight では、独自のビュークラスを登録することでデフォルトのビューエンジンを簡単に入れ替えることができます。

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 の素晴らしいところの1つは、テンプレートを変更すると自動的にキャッシュを更新することです!
  $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);
});

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(Dependency Injection Container)をお探しの場合は、Dependency Injection Container ページをご覧ください。

メソッドのマッピング

独自のシンプルなカスタムメソッドをマップするには、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がURLをルートに一致させられない場合、notFound メソッドが呼び出され、一般的な HTTP 404 応答が送信されます。この動作をオーバーライドするには、map メソッドを使用します:

Flight::map('notFound', function() {
  // カスタム404ページを表示
  include 'errors/404.html';
});

Flightは、フレームワークのコアコンポーネントを置換することもできます。 たとえば、デフォルトのルータークラスを独自のカスタムクラスで置き換えることができます:

// カスタムクラスを登録
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();

License

The MIT License (MIT)

Copyright © 2024 @mikecao, @n0nag0n

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

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

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

About

フライトとは何ですか?

フライトは、PHP向けの高速でシンプルで拡張性のあるフレームワークです。非常に汎用性が高く、あらゆる種類のWebアプリケーション構築に使用できます。単純さを念頭に置いて構築され、理解しやすく使いやすい方法で記述されています。

フライトは、PHPに新しい人やWebアプリケーション構築の方法を学びたい人にとって素晴らしい初心者向けフレームワークです。また、Webアプリケーションに対してより多くの制御を望む経験豊富な開発者にとっても優れたフレームワークです。RESTful API、シンプルなWebアプリケーション、または複雑なWebアプリケーションを簡単に構築できるように設計されています。

クイックスタート

<?php

// if installed with composer
require 'vendor/autoload.php';
// or if installed manually by zip file
// require 'flight/Flight.php';

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

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

Flight::start();

簡単ですね?ドキュメントでフライトについて詳しく学ぶ!

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

フライトフレームワークを使って始めるのに役立つ例のアプリケーションがあります。flightphp/skeleton に移動して開始方法に関する指示を参照してください!また、examples ページを訪れると、フライトで行えるいくつかのアイデアを参考にできます。

コミュニティ

Matrix Chatでの参加をお待ちしています。#flight-php-framework:matrix.org でチャットしましょう。

貢献

フライトに貢献する方法は2つあります:

  1. コアリポジトリ を訪れてコアフレームワークに貢献できます。
  2. ドキュメントに貢献することもできます。このドキュメントのウェブサイトはGitHub でホストされています。エラーを見つけたり、よりよい説明ができる場合は、修正してプルリクエストを送信してください!私たちは最新情報を提供しようと努めていますが、更新と言語翻訳は歓迎します。

必要条件

フライトはPHP 7.4以上が必要です。

注意: PHP 7.4がサポートされている理由は、執筆時点(2024年)で一部のLTS Linuxディストリビューションでデフォルトバージョンとして採用されているためです。PHP >8への移行を強制すると、これらのユーザーに多くの問題が生じる可能性があります。また、このフレームワークはPHP >8もサポートしています。

ライセンス

フライトは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

Wruczek/PHP-File-Cache

軽量でシンプルかつスタンドアロンのPHPインファイルキャッシングクラス

利点

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

インストール

Composerを介してインストールする:

composer require wruczek/php-file-cache

使用法

使用法は非常に簡単です。

use Wruczek\PhpFileCache\PhpFileCache;

$app = Flight::app();

// キャッシュが保存されるディレクトリをコンストラクタに渡します
$app->register('cache', PhpFileCache::class, [ __DIR__ . '/../cache/' ], function(PhpFileCache $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/Wruczek/PHP-File-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/index

すばらしいプラグイン

Flightは非常に拡張可能です。Flightアプリケーションに機能を追加するために使用できるプラグインがいくつかあります。いくつかはFlightチームによって公式にサポートされており、他には開始を手助けするためのミクロ/ライトライブラリがあります。

キャッシュ

キャッシュはアプリケーションの高速化に役立つ方法です。Flightと使用できるキャッシュライブラリがいくつかあります。

デバッグ

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

データベース

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

セッション

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

テンプレーティング

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

貢献

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

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/session

Ghostff/Session

PHPセッションマネージャー(非同期、フラッシュ、セグメント、セッション暗号化)。セッションデータのオプションの暗号化/復号にPHP open_sslを使用します。ファイル、MySQL、Redis、およびMemcachedをサポートしています。

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

インストール

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

composer require ghostff/session

基本的な設定

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


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

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

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

シンプルな例

以下は、この方法を使用する例です。

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

    // ここにログインロジックなどを記述します
    // パスワードの検証などを行います

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

    // セッションに書き込むたびに、明示的にコミットする必要があります。
    $session->commit();
});

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

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

    // ここに制限されたページのロジックを記述します
});

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

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

より複雑な例

以下は、この方法を使用するより複雑な例です。


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

// カスタムパスをセッション構成ファイルに設定し、セッションIDにランダムな文字列を指定します
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
        // または手動で構成オプションをオーバーライドできます
        $session->updateConfiguration([
            // セッションデータをデータベースに格納する場合(「すべてのデバイスからログアウト」のような機能が必要な場合に適しています)
            Session::CONFIG_DRIVER        => Ghostff\Session\Drivers\MySql::class,
            Session::CONFIG_ENCRYPT_DATA  => true,
            Session::CONFIG_SALT_KEY      => hash('sha256', 'my-super-S3CR3T-salt'), // これを他のものに変更してください
            Session::CONFIG_AUTO_COMMIT   => true, // 必要に応じておよび/またはセッションのコミットが難しい場合にのみ行います。
                                                   // さらにFlight::after('start', function() { Flight::session()->commit(); });を実行できます。
            Session::CONFIG_MYSQL_DS         => [
                'driver'    => 'mysql',             # PDO dns用のデータベースドライバー 例(mysql:host=...;dbname=...)
                'host'      => '127.0.0.1',         # データベースホスト
                'db_name'   => 'my_app_database',   # データベース名
                'db_table'  => 'sessions',          # データベースのテーブル
                'db_user'   => 'root',              # データベースのユーザー名
                'db_pass'   => '',                  # データベースのパスワード
                'persistent_conn'=> false,          # スクリプトがデータベースに話すたびに新しい接続を確立するオーバーヘッドを避けるため、ウェブアプリケーションが高速になります。バックサイドを見つけてください
            ]
        ]);
    }
);

ヘルプ!セッションデータが保持されません!

セッションデータを設定しており、リクエスト間で保持されていない場合は、セッションデータをコミットするのを忘れている可能性があります。これは、セッションデータを設定した後に$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/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 Panel Extensions

これはFlightを使いやすくするための拡張機能セットです。

これはパネルです

Flight Bar

それぞれのパネルはアプリケーションについて非常に役立つ情報を表示します!

Flight Data Flight Database Flight Request

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

インストール

composer require flightphp/tracy-extensions --dev を実行して、準備が整います!

設定

これを開始するために行う必要がある設定は非常に少ないです。これを使用する前に Tracy デバッガを初期化する必要があります https://tracy.nette.org/en/guide:

<?php

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

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

Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) で環境を指定する必要があるかもしれません

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

// これが全体を結びつけます
if(Debugger::$showBar === true) {
    // これは false にする必要があります、さもないとTracy が実際にレンダリングできません :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

// もっとコード

Flight::start();

追加の設定

セッションデータ

カスタムセッションハンドラー(例えばghostff/sessionなど)を持っている場合、 任意のセッションデータ配列をTracyに渡し、自動的に出力します。 TracyExtensionLoader コンストラクターの第二パラメーターで session_data キーで渡します。


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

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

if(Debugger::$showBar === true) {
    // これは false にする必要があります、さもないとTracy が実際にレンダリングできません :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}

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

Flight::start();

Latte

プロジェクトにLatteがインストールされている場合、 テンプレートを分析するためのLatteパネルを使用できます。 TracyExtensionLoader コンストラクターの第二パラメーターで latte キーでLatteインスタンスを渡すことができます。



use Latte\Engine;

require 'vendor/autoload.php';

$app = Flight::app();

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

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

if(Debugger::$showBar === true) {
    // これは false にする必要があります、さもないとTracy が実際にレンダリングできません :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

Awesome-plugins/tracy

Tracy

Tracy は Flight と一緒に使用できる素晴らしいエラーハンドラです。アプリケーションのデバッグに役立つ数々のパネルがあります。拡張して独自のパネルを追加するのも非常に簡単です。Flight チームは、flightphp/tracy-extensions プラグイン用にいくつかのパネルを作成しました。

インストール

Composer でインストールします。Tracy は本番用のエラーハンドリングコンポーネントが付属しているため、実際には dev バージョンなしでインストールする必要があります。

composer require tracy/tracy

基本設定

開始するための基本的な設定オプションがあります。詳細については、Tracy ドキュメント を参照してください。


require 'vendor/autoload.php';

use Tracy\Debugger;

// Tracy を有効にする
Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) // 明示する必要がある場合もあります(Debugger::PRODUCTION も同様)
// Debugger::enable('23.75.345.200'); // IP アドレスの配列を提供することもできます

// ここにエラーと例外が記録されます。このディレクトリが存在し、書き込み可能であることを確認してください。
Debugger::$logDirectory = __DIR__ . '/../log/';
Debugger::$strictMode = true; // すべてのエラーを表示
// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // ディプリケートされた通知を除くすべてのエラー
if (Debugger::$showBar) {
    $app->set('flight.content_length', false); // Debugger バーが表示されている場合、Flight によって content-length が設定できません。

    // これは Flight 用の Tracy 拡張機能に固有のものです。これを含めた場合は有効にしてください。
    new TracyExtensionLoader($app);
}

便利なヒント

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

Awesome-plugins/active_record

フライトアクティブレコード

アクティブレコードは、データベースエンティティをPHPオブジェクトにマッピングするものです。つまり、データベースにusersテーブルがある場合、そのテーブルの行を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', 'ユーザ名', 'パスワード');

// またはmysqli用
$database_connection = new mysqli('localhost', 'ユーザ名', 'パスワード', 'test_db');
// オブジェクトではないベースのmysqliでも
$database_connection = mysqli_connect('localhost', 'ユーザ名', 'パスワード', '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接続を渡せばOKです。

$pdo_connection = new PDO('sqlite:test.db'); // これは例です。実際はリアルなデータベース接続を使用するはずです

$User = new User($pdo_connection);

Flight PHPフレームワークを使用している場合は、ActiveRecordクラスをサービスとして登録できますが、必ずしも登録する必要はありません。

Flight::register('user', 'User', [ $pdo_connection ]);

// その後は、コントローラや関数などでこれを使うことができます。

Flight::user()->find(1);

runwayメソッド

runwayは、このライブラリ用にカスタムコマンドを持つFlight用のCLIツールです。

# 使用方法
php runway make:record データベースのテーブル名 [クラス名]

# 例
php runway make:record users

これにより、app/records/ディレクトリにUserRecord.phpという新しいクラスが作成され、次の内容が含まれます。

<?php

declare(strict_types=1);

namespace app\records;

/**
 * usersテーブル用のアクティブレコードクラス。
 * @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)

現在のレコードが取得済みかどうかを返します。

$user->find(1);
// データが見つかった場合...
$user->isHydrated(); // true

insert(): boolean|ActiveRecord

現在のレコードをデータベースに挿入します。

$user = new User($pdo_connection);
$user->name = 'デモ';
$user->password = md5('デモ');
$user->insert();
テキストベースのプライマリキー

テキストベースのプライマリキー(UUIDなど)を持っている場合、挿入前にプライマリキーの値を設定する方法が2つあります。

$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'some-uuid';
$user->name = 'デモ';
$user->password = md5('デモ');
$user->insert(); // または $user->save();

または、イベントを使用してプライマリキーを自動生成することもできます。

class User extends flight\ActiveRecord {
    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
        // このようにprimaryKeyを設定することもできます。上記の配列の代わりに
        $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 = 'デモ';
$user->password = md5('デモ');
$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

データの変更された箇所をdirtyと呼びます。

$user->greaterThan('id', 0)->orderBy('id desc')->find();

// この時点では何も"dirty"はありません。

$user->email = 'test@example.com'; // これでemailは"dirty"と見なされる
$user->update();
// 変更されたデータが保存されたため、今はdirtyなデータはありません

$user->password = password_hash()'newpassword'); // これはdirtyです
$user->dirty(); // 何も渡さないとすべてのdirtyエントリーがクリアされます。
$user->update(); // 何も変更されていないため、何も更新されません

$user->dirty([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // nameとpasswordの両方が更新されます

copyFrom(array $data): ActiveRecord (v0.4.0)

これはdirty()メソッドのエイリアスです。何を行なっているかが少しだけわかりやすいです。

$user->copyFrom([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // nameとpasswordの両方が更新されます

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インジェクション攻撃 PHP」と検索してみてください。このライブラリを使用する場合、この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)

返されるレコード数を制限します。2番目のintを指定すると、オフセットとリミットがSQLと同

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

CLI

CLIアプリケーションはアプリケーションと対話する素晴らしい方法です。これらを使用してコントローラーを生成したり、すべてのルートを表示したりすることができます。

Cookie

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

デバッグ

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

データベース

データベースはほとんどのアプリケーションの中核です。これによりデータを保存および取得できます。一部のデータベースライブラリは、クエリを記述するための単なるラッパーであり、一部は完全なORMです。

暗号化

機密データを保存するアプリケーションにとって、暗号化は重要です。データの暗号化と復号は非常に難しくありませんが、暗号化キーを適切に保存することは困難です。暗号化キーを公開ディレクトリに保存したり、コードリポジトリにコミットしたりしないように注意することが最も重要です。

セッション

セッションはAPIにとってはあまり役立ちませんが、ウェブアプリケーションを構築する際には状態を維持し、ログイン情報を保持するために重要になります。

テンプレート

テンプレートはUIを持つ任意のウェブアプリケーションの中核です。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をインストールする方法についても説明します。