Learn

フライトについて学ぶ

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

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

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

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

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

コアトピック

オートローディング

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

ルーティング

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

ミドルウェア

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

リクエスト

アプリケーション内のリクエストとレスポンスの処理方法について学びます。

レスポンス

ユーザーへのレスポンスの送り方について学びます。

HTMLテンプレート

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

セキュリティ

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

設定

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

フライトの拡張

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

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

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

依存性注入コンテナ

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

フレームワークAPI

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

v3への移行

互換性はほとんど維持されていますが、v2からv3への移行時に注意すべき変更点がいくつかあります。

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

v3への移行

後方互換性は大部分維持されていますが、v2からv3に移行する際に注意すべき変更がいくつかあります。

出力バッファリングの挙動(3.5.0)

出力バッファリング とは、PHPスクリプトによって生成された出力がクライアントに送信される前にバッファ(PHP内部)に保存されるプロセスのことです。これにより、出力がクライアントに送信される前に出力を変更することができます。

MVCアプリケーションにおいて、コントローラーは「管理者」であり、ビューの動作を管理します。コントローラーの外部(またはFlightの場合、時々匿名関数内)で出力を生成することは、MVCパターンを壊します。この変更はMVCパターンにより適合し、フレームワークをより予測可能で使いやすくするためのものです。

v2では、出力バッファリングは、自身の出力バッファを一貫して閉じていなかったため、ユニットテストストリーミング がより困難になっていました。多くのユーザーにとって、この変更は実際には影響しません。ただし、コール可能なものやコントローラーの外でコンテンツをエコーしている場合(たとえばフック内)、問題が発生する可能性があります。フック内やフレームワークが実際に実行される前にコンテンツをエコーしていた場合は過去には機能していたかもしれませんが、今後は機能しなくなります。

問題が発生する可能性のある箇所

// index.php
require 'vendor/autoload.php';

// ただの例です
define('START_TIME', microtime(true));

function hello() {
    echo 'Hello World';
}

Flight::map('hello', 'hello');
Flight::after('hello', function(){
    // これは実際には問題ありません
    echo '<p>This Hello World phrase was brought to you by the letter "H"</p>';
});

Flight::before('start', function(){
    // これのようなものはエラーを引き起こします
    echo '<html><head><title>My Page</title></head><body>';
});

Flight::route('/', function(){
    // これは実際には大丈夫です
    echo 'Hello World';

    // これも問題ありません
    Flight::hello();
});

Flight::after('start', function(){
    // これはエラーを引き起こします
    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(){
    // こちらは問題ありません
    echo '<html><head><title>My Page</title></head><body>';
});

// さらにコード 

ディスパッチャーの変更(3.7.0)

直接ディスパッチャ(Dispatcher)の静的メソッドを呼び出していた場合、Dispatcher::invokeMethod()Dispatcher::execute() など、コードを更新してこれらのメソッドを直接呼び出さないようにする必要があります。ディスパッチャ(Dispatcher)はオブジェクト指向に変換されており、より容易に依存性注入コンテナを使用できるようになっています。Dispatcherと同様の方法でメソッドを呼び出す必要がある場合は、手動で $result = $class->$method(...$params);call_user_func_array() のようなものを使用することができます。

Learn/configuration

設定

setメソッドを使用して、Flightの特定の振る舞いをカスタマイズすることができます。

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

利用可能な設定

以下は利用可能な設定のリストです:

変数

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

デフォルトではエラーはWebサーバーに記録されません。これを有効にするには、設定を変更してください:

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

見つからない場合

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

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

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

Learn/security

セキュリティ

Webアプリケーションに関するセキュリティは重要です。アプリケーションが安全であり、ユーザーデータが安全であることを確認したいと思います。Flightは、Webアプリケーションのセキュリティを強化するためのさまざまな機能を提供しています。

ヘッダー

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

セキュリティヘッダーのセキュリティを確認するための2つの優れたウェブサイトは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にセッションオブジェクトを作成・アタッチしたと仮定)
// セッションごとに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トークンを出力するカスタム関数を設定
// 注:ViewはビューエンジンとしてLatteが設定されています
Flight::view()->addFunction('csrf', function() {
    $csrfToken = Flight::session()->get('csrf_token');
    return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
});

そして、Latteテンプレートで csrf() 関数を使用してCSRFトークンを出力できます。

<form method="post">
    {csrf()}
    <!-- その他のフォームフィールド -->
</form>

短くてシンプルですね?

CSRFトークンのチェック

イベントフィルタを使用してCSRFトークンを検証できます:

// このミドルウェアはリクエストがPOSTリクエストかどうかを確認し、
// 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');
        }
    }
});

または、ミドルウェアクラスを使用することもできます:

// 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のviewクラスを使用すると、XSS攻撃を防ぐために出力を簡単にエスケープできます。

// ユーザーが賢いとして、これを名前として使用しようとしていると仮定します
$name = '<script>alert("XSS")</script>';

// これは出力をエスケープします
Flight::view()->set('name', $name);
// これが出力されます: &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

// Viewクラスとして登録されているLatteを使用する場合は、これも自動的にエスケープされます。
Flight::view()->render('template', ['name' => $name]);

SQLインジェクション

SQLインジェクションは、悪意のあるユーザーがSQLコードをデータベースに注入できる攻撃の一種です。これは、データベースから情報を盗んだり、データベースでアクションを実行したりするために使用される可能性があります。再度、ユーザーからの入力を絶対に信頼しないでください! 常に彼らが徹底的に検証しているものと仮定してください。SQLインジェクションを防止するために PDO オブジェクトでプリペアドステートメントを使用できます。

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

Cross-Origin Resource Sharing (CORS) は、Webページの多くのリソース(フォント、JavaScriptなど)が、リソースが元々起動したドメインの外部ドメインから要求されることを許可するメカニズムです。Flightには組み込み機能は存在しませんが、このようなCORS攻撃から保護するためのミドルウェアやイベントフィルタを使って簡単に処理できます。

// app/middleware/CorsMiddleware.php

namespace app\middleware;

class CorsMiddleware
{
    public function before(array $params): void
    {
        $response = Flight::response();
        if (isset($_SERVER['HTTP_ORIGIN'])) {
            $this->allowOrigins();
            $response->header('Access-Control-Allow-Credentials: true');
            $response->header('Access-Control-Max-Age: 86400');
        }

        if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD'])) {
                $response->header(
                    'Access-Control-Allow-Methods: GET, POST, PUT, DELETE, PATCH, OPTIONS'
                );
            }
            if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS'])) {
                $response->header(
                    "Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}"
                );
            }
            $response->send();
            exit(0);
        }
    }

    private function allowOrigins(): void
    {
        // ここで許可されるホストをカスタマイズしてください。
        $allowed = [
            'capacitor://localhost',
            'ionic://localhost',
            'http://localhost',
            'http://localhost:4200',
            'http://localhost:8080',
            'http://localhost:8100',
        ];

        if (in_array($_SERVER['HTTP_ORIGIN'], $allowed)) {
            $response = Flight::response();
            $response->header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
        }
    }
}

// index.php またはルートがある場所
Flight::route('/users', function() {
    $users = Flight::db()->fetchAll('SELECT * FROM users');
    Flight::json($users);
})->addMiddleware(new CorsMiddleware());

結論

セキュリティは重要であり、Webアプリケーションが安全であることを確認することが重要です。Flightは、Webアプリケーションをセキュアにするためのさまざまな機能を提供していますが、常に警戒して、

Learn/overriding

オーバーライド

Flightは、コードを変更することなく、独自のニーズに合わせてデフォルトの機能をオーバーライドできるようにします。

たとえば、FlightがURLをルートにマッチさせられない場合、notFoundメソッドが呼び出され、一般的な HTTP 404 レスポンスが送信されます。この動作を以下のようにオーバーライドできます:

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

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

// カスタムクラスを登録
Flight::register('router', MyRouter::class);

// FlightがRouterインスタンスをロードするとき、あなたのクラスがロードされます
$myrouter = Flight::router();

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

Learn/routing

ルーティング

注意: ルーティングについて詳しく知りたいですか?より詳細な説明については、"なぜフレームワークか?" ページをチェックしてください。

Flight での基本的なルーティングは、URL パターンをコールバック関数またはクラスとメソッドの配列と一致させることで行われます。

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

ルートは定義された順に一致します。リクエストと一致する最初のルートが呼び出されます。

コールバック/関数

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

function hello(){
    echo 'こんにちは、世界!';
}

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

クラス

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

class Greeting {
    public static function hello() {
        echo 'こんにちは、世界!';
    }
}

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

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


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

    public function hello() {
        echo "こんにちは、{$this->name}さん!";
    }
}

// index.php
$greeting = new Greeting();

Flight::route('/', [ $greeting, 'hello' ]);
// オブジェクトを作成せずにこれを行うこともできます
// 注意:コンストラクタに引数は挿入されません
Flight::route('/', [ 'Greeting', '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 に関する詳細については依存性注入ページを参照してください
$dice = new \Dice\Dice();

// 変数を '$dice = ' で再割り当てするのを忘れないでください!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
    'shared' => true,
    'constructParams' => [ 
        'mysql:host=localhost;dbname=test', 
        'root',
        'password'
    ]
]);

// コンテナハンドラを登録します
Flight::registerContainerHandler(function($class, $params) use ($dice) {
    return $dice->create($class, $params);
});

// 通常どおりにルーティングを行います
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// または
Flight::route('/hello/@id', 'Greeting->hello');
// または
Flight::route('/hello/@id', 'Greeting::hello');

Flight::start();

メソッドルーティング

デフォルトでは、ルートパターンはすべてのリクエストメソッドと一致します。特定のメソッドに応答するには、URL の前に識別子を配置します。

Flight::route('GET /', function () {
  echo 'GET リクエストを受け取りました。';
});

Flight::route('POST /', function () {
  echo 'POST リクエストを受け取りました。';
});

// ルートを作成するために Flight::get() を使用することはできません
//    それはルートを作成するのではなく変数を取得するためのメソッドです。
// Flight::post('/', function() { /* コード */ });
// Flight::patch('/', function() { /* コード */ });
// Flight::put('/', function() { /* コード */ });
// Flight::delete('/', function() { /* コード */ });

| デリミタを使用して、複数のメソッドを単一のコールバックにマップすることもできます:

Flight::route('GET|POST /', function () {
  echo 'GET または POST リクエストを受け取りました。';
});

さらに、使用できるヘルパーメソッドを持つ Router オブジェクトを取得することもできます:


$router = Flight::router();

// すべてのメソッドをマップします
$router->map('/', function() {
    echo 'こんにちは、世界!';
});

// GET リクエスト
$router->get('/users', function() {
    echo 'ユーザー';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();

正規表現

ルートで正規表現を使用することができます:

Flight::route('/user/[0-9]+', function () {
  // これは /user/1234 に一致します
});

このメソッドは利用可能ですが、名前付きパラメーター、または 名前付きパラメーターと正規表現を使用することをお勧めします。それらはより読みやすく、メンテナンスが容易です。

名前付きパラメーター

コールバック関数に渡される名前付きパラメーターを指定できます。

Flight::route('/@name/@id', function (string $name, string $id) {
  echo "$name さん、こんにちは($id)!";
});

: デリミタを使用して、名前付きパラメーターに正規表現を含めることもできます:

Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
  // これは /bob/123 に一致します
  // ただし /bob/12345 には一致しません
});

注意: 名前付きパラメーターと正規表現とを組み合わせた括弧のグループ () はサポートされていません。 :'(

オプションパラメーター

マッチングをオプションにするために、セグメントを括弧で囲んで名前付きパラメーターを指定できます。

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

一致しなかった任意のオプションパラメーターは NULL として渡されます。

ワイルドカード

マッチングは個々の URL セグメントにのみ行われます。複数セグメントを一致させたい場合は、* ワイルドカードを使用できます。

Flight::route('/blog/*', function () {
  // これは /blog/2000/02/01 に一致します
});

すべてのリクエストを単一のコールバックにルーティングするには、次のようにします:

Flight::route('*', function () {
  // 何かを行う
});

パッシング

コールバック関数から true を返すことで、次の一致するルートに実行を引き継ぐことができます。

Flight::route('/user/@name', function (string $name) {
  // 一部の条件を確認
  if ($name !== "Bob") {
    // 次のルートに続行
    return true;
  }
});

Flight::route('/user/*', function () {
  // これが呼び出されます
});

ルートにエイリアスを付ける

ルートにエイリアスを割り当てることができ、後でコード内で動的に URL を生成できます(たとえば、テンプレートなど)。

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

// 何かのコードの後で
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5' が返されます

特に URL が変更される場合に役立ちます。上記の例では、ユーザーは /admin/users/@id に移動されました。 エイリアスを使用すると、エイリアスを参照しているどこかすべてを変更する必要がないため、上記の例のようにエイリアスは /admin/users/5 を返します。

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

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo '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_bufferingfalse に設定している場合にのみ利用できます。

Flight::route('/stream-users', function() {

    // ルートの後で追加のヘッダーを設定する場合、ルートの実行後にそれらを定義する必要があります。
    // すべてが echo される前に、header() 関数を直接呼び出すか
    // Flight::response()->setRealHeader() を呼び出す必要があります。
    header('Content-Disposition: attachment; filename="users.json"');
    // または
    Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="users.json"');

    // データを取得する方法にかかわらず、例として...
    $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# ルーティング

> **注意:** ルーティングについてさらに理解したいですか?詳細な説明は、[フレームワークの必要性](/learn/why-frameworks) ページをご覧ください。

Flight での基本的なルーティングは、URL パターンとコールバック関数またはクラスとメソッドの配列を一致させることで行われます。

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

ルートは定義順に一致します。リクエストと一致する最初のルートが呼び出されます。

コールバック/関数

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

function hello(){
    echo 'こんにちは、世界!';
}

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

クラス

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

class Greeting {
    public static function hello() {
        echo 'こんにちは、世界!';
    }
}

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

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


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

    public function hello() {
        echo "こんにちは、{$this->name}さん!";
    }
}

// index.php
$greeting = new Greeting();

Flight::route('/', [ $greeting, 'hello' ]);
// オブジェクトを作成せずにこれを行うこともできます
// 注意: 引数はコンストラクタに注入されません
Flight::route('/', [ 'Greeting', '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 については、依存性注入ページで詳細をご覧ください
$dice = new \Dice\Dice();

// 変数を '$dice = ' で再割り当てするのを忘れないでください!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
    'shared' => true,
    'constructParams' => [ 
        'mysql:host=localhost;dbname=test', 
        'root',
        'password'
    ]
]);

// コンテナハンドラを登録します
Flight::registerContainerHandler(function($class, $params) use ($dice) {
    return $dice->create($class, $params);
});

// 通常と同じようにルーティングを行います
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// または
Flight::route('/hello/@id', 'Greeting->hello');
// または
Flight::route('/hello/@id', 'Greeting::hello');

Flight::start();

メソッドルーティング

デフォルトでは、ルートパターンはすべてのリクエストメソッドに一致します。特定のメソッドに応答するには、URL の前に識別子を配置します。

Flight::route('GET /', function () {
  echo 'GET リクエストを受信しました。';
});

Flight::route('POST /', function () {
  echo 'POST リクエストを受信しました。';
});

// ルートを作成するために Flight::get() を使用することはできません
//    それはルートを作成するのではなく変数を取得するためのメソッドです。
// Flight::post('/', function() { /* コード */ });
// Flight::patch('/', function() { /* コード */ });
// Flight::put('/', function() { /* コード */ });
// Flight::delete('/', function() { /* コード */ });

| デリミタを使用して、複数のメソッドを単一のコールバックにマップすることもできます:

Flight::route('GET|POST /', function () {
  echo 'GET または POST リクエストを受信しました。';
});

さらに、使用できるヘルパーメソッドを持つ Router オブジェクトを取得することもできます:


$router = Flight::router();

// すべてのメソッドをマップします
$router->map('/', function() {
    echo 'こんにちは、世界!';
});

// GET リクエスト
$router->get('/users', function() {
    echo 'ユーザー';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();

正規表現

ルートで正規表現を使用することができます:

Flight::route('/user/[0-9]+', function () {
  // これは /user/1234 に一致します
});

このメソッドは利用可能ですが、名前付きパラメーター、または 名前付きパラメーターと正規表現を使用することをお勧めします。それらはより読みやすく、メンテナンスが容易です。

名前付きパラメーター

コールバック関数に渡される名前付きパラメーターを指定できます。

Flight::route('/@name/@id', function (string $name, string $id) {
  echo "$name さん、こんにちは($id)!";
});

: デリミタを使用して、名前付きパラメーターに正規表現を含めることもできます:

Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
  // これは /bob/123 に一致します
  // ただし /bob/12345 には一致しません
});

注意: 名前付きパラメーターと正規表現とを組み合わせた括弧のグループ () はサポートされていません。 :'(

オプションパラメーター

マッチングをオプションにするために、セグメントを括弧で囲んで名前付きパラメーターを指定できます。

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

一致しなかった任意のオプションパラメーターは NULL として渡されます。

ワイルドカード

マッチングは個々の URL セグメントにのみ行われます。複数のセグメントを一致させたい場合は、* ワイルドカードを使用できます。

Flight::route('/blog/*', function () {
  // これは /blog/2000/02/01 に一致します
});

すべてのリクエストを単一のコールバックにルーティングするには、次のようにします:

Flight::route('*', function () {
  // 何かを行う
});

パッシング

コールバック関数から true を返すことで、次の一致するルートに実行を引き継ぐことができます。

Flight::route('/user/@name', function (string $name) {
  // 一部の条件を確認
  if ($name !== "Bob") {
    // 次のルートへ継続
    return true;
  }
});

Flight::route('/user/*', function () {
  // これが呼び出されます
});

ルートにエイリアスを付ける

ルートにエイリアスを割り当てることができ、後でコード内で動的に URL を生成できます(たとえば、テンプレートなど)。

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

// 何かのコードの後で
Flight::getUrl('user_view', [ 'id' => 5 ]); // '/users/5' が返されます

特に URL が変更される場合に役立ちます。上記の例では、ユーザーは /admin/users/@id に移動されました。 エイリアスを使用すると、エイリアスを参照しているどこかすべてを変更する必要がないため、上記の例のようにエイリアスは /admin/users/5 を返します。

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

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo '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_bufferingfalse に設定している場合にのみ利用できます。


Flight::route('/stream-users', function() {

    // ルートの後で追加のヘッダーを設定する場合、ルートの実行後にそれらを定義する必要があります。
    // すべてが echo される前に、header() 関数を直接呼び出すか
    // Flight::response()->setRealHeader() を呼び出す必要があります。
    header('Content-Disposition: attachment; filename="users.json"');
    // または
    Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="users.json"');

    // データを取得する方法にかかわらず、例として...
    $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',
    // オプションのステータスコード、デフォルトは 200
    'status' => 200
]);

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のライブラリの例には、DicePimplePHP-DI、および league/container があります。

DICは、クラスを中央集権的に作成および管理できるというファンシーな方法です。これは、同じオブジェクトを複数のクラス(たとえば、コントローラ)に渡す必要がある場合に便利です。単純な例がこれをより理解しやすくするかもしれません。

基本的な例

従来のやり方は次のように見えるかもしれません:


require 'vendor/autoload.php';

// データベースからユーザーを管理するクラス
class UserController {

    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function view(int $id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $id]);

        print_r($stmt->fetch());
    }
}

$User = new UserController(new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'));
Flight::route('/user/@id', [ $UserController, 'view' ]);

Flight::start();

上記のコードからわかるように、新しいPDOオブジェクトを作成してUserControllerクラスに渡しています。これは小規模なアプリケーションでは問題ありませんが、アプリケーションが成長すると、複数の場所で同じPDOオブジェクトを作成していることに気づくでしょう。ここでDICが役立ちます。

以下は、DICを使用した同じ例(Diceを使用)です:


require 'vendor/autoload.php';

// 上記と同じクラス。何も変更されていません
class UserController {

    protected PDO $pdo;

    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
    }

    public function view(int $id) {
        $stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
        $stmt->execute(['id' => $id]);

        print_r($stmt->fetch());
    }
}

// 新しいコンテナを作成
$container = new \Dice\Dice;
// 以下のように再代入するのを忘れないでください!
$container = $container->addRule('PDO', [
    // shared は、同じオブジェクトが毎回返されることを意味します
    'shared' => true,
    'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);

// これにより、Flightがそれを使用することを知るようにコンテナハンドラが登録されます。
Flight::registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

// これでコンテナを使用してUserControllerを作成できます
Flight::route('/user/@id', [ 'UserController', 'view' ]);
// または、次のようにルートを定義することもできます
Flight::route('/user/@id', 'UserController->view');
// または
Flight::route('/user/@id', 'UserController::view');

Flight::start();

Learn/middleware

ルートミドルウェア

Flightはルートおよびグループルートミドルウェアをサポートしています。ミドルウェアは、ルートのコールバックの前(または後)に実行される関数です。これは、API認証チェックをコードに追加したり、ユーザーがルートにアクセスする権限を持っているかを検証したりする素晴らしい方法です。

基本ミドルウェア

以下は基本的な例です:

// 匿名関数のみを指定すると、ルートのコールバックの前に実行されます。
// 下記のクラスを除くと「後」のミドルウェア関数はありません
Flight::route('/path', function() { echo 'ここにいます!'; })->addMiddleware(function() {
    echo '最初にミドルウェア!';
});

Flight::start();

// これにより、「最初にミドルウェア!ここにいます!」と出力されます。

ミドルウェアに関して、使用する前に把握しておくべき非常に重要な注意点がいくつかあります:

ミドルウェアクラス

ミドルウェアはクラスとしても登録できます。"後"の機能性が必要な場合、クラスを使用する必要があります。

class MyMiddleware {
    public function before($params) {
        echo '最初にミドルウェア!';
    }

    public function after($params) {
        echo '最後にミドルウェア!';
    }
}

$MyMiddleware = new MyMiddleware();
Flight::route('/path', function() { echo 'ここにいます!'; })->addMiddleware($MyMiddleware); // または ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);

Flight::start();

// これにより、「最初にミドルウェア!ここにいます!最後にミドルウェア!」と表示されます。

ミドルウェアのグループ化

ルートグループを追加し、そのグループ内のすべてのルートに同じミドルウェアを付け加えることができます。これは、ヘッダーのAPIキーをチェックするためなど、いくつかのルートをAuthミドルウェアでグループ化する必要がある場合に便利です。


// グループメソッドの最後に追加されました
Flight::group('/api', function() {

    // この「空」に見えるルートは実際に/apiに一致します
    Flight::route('', function() { echo 'api'; }, false, 'api');
    Flight::route('/users', function() { echo 'users'; }, false, 'users');
    Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);

すべてのルートにグローバルミドルウェアを適用したい場合は、"空"のグループを追加できます:


// グループメソッドの最後に追加されました
Flight::group('', function() {
    Flight::route('/users', function() { echo 'users'; }, false, 'users');
    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();

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

querydatacookiesfiles プロパティには、配列やオブジェクトとしてアクセスできます。

そのため、クエリ文字列パラメータを取得するには、次のようにします:

$id = Flight::request()->query['id'];

または、次のようにもできます:

$id = Flight::request()->query->id;

RAW リクエストボディ

例えば PUT リクエストを処理する場合など、生の HTTP リクエストボディを取得するには、次のようにします:

$body = Flight::request()->getBody();

JSON 入力

タイプが application/json でデータが {"id": 123} としてリクエストを送信した場合、それは data プロパティから利用できます:

$id = Flight::request()->data->id;

$_SERVER へのアクセス

getVar() メソッドを介して $_SERVER 配列にアクセスするショートカットが利用可能です:

$host = Flight::request()->getVar['HTTP_HOST'];

リクエストヘッダへのアクセス

getHeader() または getHeaders() メソッドを使用してリクエストヘッダにアクセスできます:

// たとえば、Authorization ヘッダが必要な場合
$host = Flight::request()->getHeader('Authorization');

// すべてのヘッダを取得する必要がある場合
$headers = Flight::request()->getHeaders();

Learn/frameworkmethods

フレームワークのメソッド

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

コアメソッド

Flight::map(string $name, callable $callback, bool $pass_route = false) // カスタムフレームワークメソッドを作成します。
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // クラスをフレームワークメソッドに登録します。
Flight::before(string $name, callable $callback) // フレームワークメソッドの前にフィルタを追加します。
Flight::after(string $name, callable $callback) // フレームワークメソッドの後にフィルタを追加します。
Flight::path(string $path) // クラスの自動読み込み用のパスを追加します。
Flight::get(string $key) // 変数を取得します。
Flight::set(string $key, mixed $value) // 変数を設定します。
Flight::has(string $key) // 変数が設定されているかどうかを確認します。
Flight::clear(array|string $key = []) // 変数をクリアします。
Flight::init() // フレームワークをデフォルト設定に初期化します。
Flight::app() // アプリケーションオブジェクトのインスタンスを取得します。

拡張メソッド

Flight::start() // フレームワークを開始します。
Flight::stop() // フレームワークを停止してレスポンスを送信します。
Flight::halt(int $code = 200, string $message = '') // オプションのステータスコードとメッセージでフレームワークを停止します。
Flight::route(string $pattern, callable $callback, bool $pass_route = false) // URLパターンをコールバックにマップします。
Flight::group(string $pattern, callable $callback) // URLのグループを作成します。パターンは文字列でなければなりません。
Flight::redirect(string $url, int $code) // 別のURLにリダイレクトします。
Flight::render(string $file, array $data, ?string $key = null) // テンプレートファイルをレンダリングします。
Flight::error(Throwable $error) // HTTP 500レスポンスを送信します。
Flight::notFound() // HTTP 404レスポンスを送信します。
Flight::etag(string $id, string $type = 'string') // ETag HTTPキャッシュを実行します。
Flight::lastModified(int $time) // 最終変更日HTTPキャッシュを実行します。
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // JSONレスポンスを送信します。
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // JSONPレスポンスを送信します。

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

Learn/api

フレームワーク API メソッド

Flight は使いやすく、理解しやすいように設計されています。以下はフレームワークの完全な メソッドセットです。これには、通常の静的メソッドであるコアメソッドと、フィルター処理を したりオーバーライドできるマップされたメソッドである拡張可能なメソッドが含まれています。

コアメソッド

これらのメソッドはフレームワークの中心部であり、オーバーライドできません。

Flight::map(string $name, callable $callback, bool $pass_route = false) // カスタムフレームワークメソッドを作成します。
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // クラスをフレームワークメソッドに登録します。
Flight::unregister(string $name) // クラスをフレームワークメソッドから登録解除します。
Flight::before(string $name, callable $callback) // フレームワークメソッドの前にフィルターを追加します。
Flight::after(string $name, callable $callback) // フレームワークメソッドの後にフィルターを追加します。
Flight::path(string $path) // クラスの自動読み込みのためのパスを追加します。
Flight::get(string $key) // 変数を取得します。
Flight::set(string $key, mixed $value) // 変数を設定します。
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::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/why_frameworks

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

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

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

ここに、フレームワークを考慮する理由のいくつかがあります:

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

ルーティングとは?

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

以下のように機能するかもしれません:

そして、なぜ重要なのか?

適切な中央集権型のルーターを持っていると、実際には生活が格段に簡単になるかもしれません! 最初は見えにくいかもしれませんが、ここにいくつかの理由があります:

おそらく、ウェブサイトを作成する際のスクリプトごとの方法には慣れているはずです。index.php というファイルがあり、URLをチェックし、URLに基づいて特定の機能を実行する if ステートメントがいくつかあるかもしれません。これは一種のルーティングですが、整理されていないため、すぐに手に負えなくなる可能性があります。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);
}

// その他...

それとこれ?

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

// おそらく、app/controllers/UserController.php 内に
class UserController {
    public function viewUserProfile($id) {
        // 何かする
    }

    public function editUserProfile($id) {
        // 何かする
    }
}

中央集権型のルーティングシステムを使用する利点が見えてくると思います。長期的に管理および理解するのがはるかに簡単です!

リクエストとレスポンス

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

リクエスト

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

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

レスポンス

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

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

Learn/httpcaching

HTTP キャッシング

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

Last-Modified

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

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

ETag

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

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

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

Learn/responses

レスポンス

Flightは、レスポンスのヘッダーの一部を生成するのを助けますが、ユーザーに返す内容の大部分はあなたがコントロールします。時にはResponseオブジェクトに直接アクセスすることもできますが、ほとんどの場合、Flightインスタンスを使用してレスポンスを送信します。

基本的なレスポンスを送信する

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


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

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

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


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

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

ステータスコード

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

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

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

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

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

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


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

JSON

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

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

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, 'Be right back...');

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

Flight::stop();

HTTPキャッシュ

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

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

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


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

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

最終変更

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

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

ETag

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

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

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

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

ビュー

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' => '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 テンプレートエンジンをビューに使用します:

// 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 のデフォルトのレンダリングメソッドもオーバーライドする必要があります:

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

拡張

Flight は拡張可能なフレームワークとして設計されています。このフレームワークにはデフォルトのメソッドやコンポーネントが付属していますが、独自のメソッドをマップしたり、独自のクラスを登録したり、既存のクラスやメソッドを上書きしたりすることが可能です。

DIC(Dependency Injection Container)をお探しの場合は、Dependency Injection Container ページをご覧ください。

メソッドのマッピング

独自のシンプルなカスタムメソッドをマッピングするには、map 関数を使用します:

// メソッドをマップ
Flight::map('hello', function (string $name) {
  echo "hello $name!";
});

// カスタムメソッドを呼び出す
Flight::hello('Bob');

これは、メソッドに変数を渡して予期される値を取得する必要がある場合により使用されます。以下のように register() メソッドを使用することは、構成を渡し、それから事前に構成されたクラスを呼び出すためのものです。

クラスの登録

独自のクラスを登録して構成するには、register 関数を使用します:

// クラスを登録
Flight::register('user', User::class);

// クラスのインスタンスを取得する
$user = Flight::user();

登録メソッドでは、クラスのコンストラクタにパラメータを渡すこともできます。したがって、カスタムクラスを読み込むときに事前に初期化されます。追加の配列を渡すことでコンストラクタパラメータを定義できます。以下はデータベース接続の読み込み例です:

// コンストラクタパラメータ付きのクラスを登録
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 がルーターインスタンスをロードするとき、あなたのクラスがロードされます
$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/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
 */

// 名前空間は必要ありません

// オートロードされるすべてのクラスは、各単語の先頭を大文字にしてパスカルケースにすることが推奨されます(単語間にスペースはありません)
// クラス名にアンダースコアを含めることはできないという要件もあります
class MyController {

    public function index() {
        // 何かをする
    }
}

名前空間

名前空間がある場合、これを実装するのは実際には非常に簡単です。アプリケーションのルートディレクトリ(ドキュメントルートやpublic/フォルダではない)を指定するためにFlight::path()メソッドを使用する必要があります。


/**
 * public/index.php
 */

// オートローダーにパスを追加
Flight::path(__DIR__.'/../');

これがコントローラーの見た目です。以下の例を見てくださいが、重要な情報にはコメントを注目してください。

/**
 * app/controllers/MyController.php
 */

// 名前空間が必要です
// 名前空間はディレクトリ構造と同じです
// 名前空間はディレクトリ構造と同じパターンである必要があります
// 名前空間とディレクトリにアンダースコアを含めることはできません
namespace app\controllers;

// オートロードされるすべてのクラスは、各単語の先頭を大文字にしてパスカルケースにすることが推奨されます(単語間にスペースはありません)
// クラス名にアンダースコアを含めることはできないという要件もあります
class MyController {

    public function index() {
        // 何かをする
    }
}

そして、あなたがutilsディレクトリにあるクラスをオートロードしたい場合、基本的に同じことを行います:


/**
 * app/UTILS/ArrayHelperUtil.php
 */

// 名前空間はディレクトリ構造とケースと一致している必要があります(ファイルツリーでUTILSディレクトリが全部大文字になっていることに注意してください)
namespace app\UTILS;

class ArrayHelperUtil {

    public function changeArrayCase(array $array) {
        // 何かをする
    }
}

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

著作権 © `2023` `@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にアクセスしてください!また、Flightでできることのいくつかのインスピレーションに関する資料があるexamplesページもご覧いただけます。

コミュニティ

私たちはMatrix上にいます!私たちとチャットするには、#flight-php-framework:matrix.orgにアクセスしてください。

貢献

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

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

必要条件

Flightを利用するには、PHP 7.4以上が必要です。

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

ライセンス

FlightはMITライセンスのもとでリリースされています。

Awesome-plugins/php_cookie

クッキー

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

インストール

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

composer require overclokk/cookie

使用法

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


use Overclokk\Cookie\Cookie;

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

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

/**
 * ExampleController.php
 */

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

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

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

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

Awesome-plugins/php_encryption

PHP 暗号化

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

インストール

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

composer require defuse/php-encryption

セットアップ

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

vendor/bin/generate-defuse-key

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

使用方法

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


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

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

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

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

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

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

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

Awesome-plugins/php_file_cache

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

すばらしいプラグイン

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

キャッシュ

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

デバッグ

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

データベース

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

セッション

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

テンプレーティング

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

貢献

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

Awesome-plugins/pdo_wrapper

PdoWrapper PDO ヘルパークラス

Flight には PDO のヘルパークラスが付属しています。これにより、データベースへのクエリを簡単に実行することができます 全ての prepared/execute/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、または while ループ内で SELECT を使用する場合に使用します

$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を使用します。File、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,          # スクリプトがデータベースと通信するたびに新しい接続を確立する手間を避け、より速いWebアプリケーションを実現します。バックサイドを見つけてください
            ]
        ]);
    }
);

ドキュメント

完全なドキュメントのためにGitHub Readmeを参照してください。構成オプションはdefault_config.phpファイルでよく文書化されています。パッケージ自体を調べたい場合は、コードは理解しやすいです。

Awesome-plugins/tracy_extensions

Tracy フライトパネル拡張機能

これは、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 コンストラクタの2番目のパラメータ内の 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 コンストラクタの2番目のパラメータで、latte キーで Latte インスタンスを渡すことができます。



use Latte\Engine;

require 'vendor/autoload.php';

$app = Flight::app();

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

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

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

Awesome-plugins/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 オブジェクトに「変換」することができます。基本例を参照してください。

基本例

以下のテーブルがあると仮定します:

CREATE TABLE users (
    id INTEGER PRIMARY KEY, 
    name TEXT, 
    password TEXT 
);

これを表す新しいクラスを設定できます:

/**
 * アクティブレコードクラスは通常単数形です
 * 
 * テーブルのプロパティをここにコメントとして追加することを強くお勧めします
 * 
 * @property int    $id
 * @property string $name
 * @property string $password
 */ 
class User extends flight\ActiveRecord {
    public function __construct($database_connection)
    {
        // この方法で設定できます
        parent::__construct($database_connection, 'users');
        // またはこの方法で
        parent::__construct($database_connection, null, [ 'table' => 'users']);
    }
}

そこで、魔法が起こります!

// sqlite の場合
$database_connection = new PDO('sqlite:test.db'); // これは例です。実際には実際のデータベース接続を使用するでしょう

// mysql の場合
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');

// または mysqli を使用する場合
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// またはオブジェクトを使用しない mysqli の場合
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');

$user = new User($database_connection);
$user->name = 'ボビー テーブルズ';
$user->password = password_hash('some cool password');
$user->insert();
// または $user->save();

echo $user->id; // 1

$user->name = 'ジョセフ マンマ';
$user->password = password_hash('some cool password again!!!');
$user->insert();
// ここでは $user->save() を使用することはできません!

echo $user->id; // 2

新しいユーザーを追加するのはとても簡単でしたね!では、データベースにユーザー行があるので、それを取り出すにはどうすればよいでしょうか?

$user->find(1); // データベース内の id = 1 のレコードを見つけて返します。
echo $user->name; // 'ボビー テーブルズ'

すべてのユーザーを見つけたい場合はどうすればよいですか?

$users = $user->findAll();

特定の条件で見つける場合はどうでしょうか?

$users = $user->like('name', '%mamma%')->findAll();

楽しいでしょう?インストールして始めましょう!

インストール

Composer で簡単にインストールできます

composer require flightphp/active-record 

使用法

これは独立したライブラリとして使用するか、Flight PHP フレームワークと共に使用できます。完全にあなた次第です。

独立した使用法

コンストラクタに PDO 接続を渡すだけです。

$pdo_connection = new PDO('sqlite:test.db'); // これは例です。実際には実際のデータベース接続を使用します

$User = new User($pdo_connection);

Flight PHP フレームワーク

Flight PHP フレームワークを使用している場合、ActiveRecord クラスをサービスとして登録できます(しかし、率直に言って必要はありません)。

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

// 次に、コントローラーや関数などで次のように使用できます。

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

CRUD 関数

find($id = null) : boolean|ActiveRecord

1 つのレコードを検索して現在のオブジェクトに割り当てます。何かの $id を渡すと、その値と主キーでの検索を実行します。何も渡さない場合、テーブルの最初のレコードを検索します。

その他のヘルパーメソッドを渡してテーブルをクエリできます。

// 事前にいくつかの条件付きレコードを検索
$user->notNull('password')->orderBy('id DESC')->find();

// 特定の id でレコードを検索
$id = 123;
$user->find($id);

findAll(): array<int,ActiveRecord>

指定したテーブル内のすべてのレコードを検索します。

$user->findAll();

isHydrated(): boolean (v0.4.0)

現在のレコードがハイドレーション(データベースから取得)された場合、true を返します。

$user->find(1);
// データが含まれるレコードが見つかると...
$user->isHydrated(); // true

insert(): boolean|ActiveRecord

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

$user = new User($pdo_connection);
$user->name = 'デモ';
$user->password = md5('デモ');
$user->insert();

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

ダーティデータは、レコード内で変更されたデータを指します。

$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(); // 何も dirty としてキャプチャされなかったため、何も更新されません

$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 インジェクション攻撃に対して脆弱です。インジェクション攻撃に関する多くの記事がオンラインであり、"php sql injection attacks" などで検索すると、多くの記事が見つかります。このライブラリでこれを扱う正しい方法は、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 番目の整数が渡されると、オフセット、limit と同じく SQL。

$user->orderby('name DESC')->limit(0, 10)->findAll();

WHERE 条件

equal(string $field, mixed $value) / eq(string $field, mixed $value)

field = $value が成り立つ場合。

$user->eq('id', 1)->find();

notEqual(string $field, mixed $value) / ne(string $field, mixed $value)

field <> $value が成り立つ場合。

$user->ne('id', 1)->find();

isNull(string $field)

field IS NULL が成り立つ場合。

$user->isNull('id')->find();

isNotNull(string $field) / notNull(string $field)

field IS NOT NULL が成り立つ場合。

$user->isNotNull('id')->find();

greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)

field > $value が成り立つ場合。

$user->gt('id', 1)->find();

lessThan(string $field, mixed $value) / lt(string $field, mixed $value)

field < $value が成り立つ場合。

$user->lt('id', 1)->find();

greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)

field >= $value が成り立つ場合。

$user->ge('id', 1)->find();

lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)

field <= $value が成り立つ場合。

$user->le('id', 1)->find();

like(string $field, mixed $value) / notLike(string $field, mixed $value)

field LIKE $value または field NOT LIKE $value が成り立つ場合。

$user->like('name', 'de')->find();

in(string $field, array $values) / notIn(string $field, array $values)

field IN($value) または field NOT IN($value) が成り立つ場合。

$user->in('id', [1, 2])->find();

between(string $field, array $values)

field BETWEEN $value AND $value1 が成り立つ場合。

$user->between('id', [1, 2])->find();

リレーションシップ

このライブラリを使用してさまざまな種類のリレーションシップを設定できます。テーブル間の oneto-many および one-to-one リレーションシップを設定できます。これには、クラスの事前設定が少し必要ですが、構文を推測することが難しいかもしれません。

$relations 配列を設定することは難しくありませんが、正しい構文を推測することは混乱するかもしれません。

protected array $relations = [
    // キーは何でもかまいません。ActiveRecord の名前がおそらく適しています。例: user, contact, client
    'user' => [
        // 必須
        // self::HAS_MANY、self::HAS_ONE、self::BELONGS_TO
        self::HAS_ONE, // これがリレーションシップの種類です

        // 必須
        'Some_Class', // これが参照する「他の」ActiveRecord クラスです

        // 必須
        // リレーションシップの種類に応じて
        // self::HAS_ONE = 結合を参照する外部キー
        // self::HAS_MANY = 結合を参照する外部キー
        // self::BELONGS_TO = 結合を示すローカルキー
        'local_or_foreign_key',
        // FYI、これも「他の」モデルの主キーにのみ結合されます

        // オプション
        // リレーションシップを結合する際に追加の条件を追加します
        // 例: $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))
        [ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' => 5 ],

        // オプション
        'back_reference_name' // これは、リレーションシップを逆参照して自分自身に戻りたい場合に使用します。例: $user->contact->user;
    ];
]
class User extends ActiveRecord {
    protected array $relations = [
        'contacts' => [ self::HAS_MANY, Contact::class, 'user_id' ],
        'contact' => [ self::HAS_ONE, Contact::class, 'user_id' ],
    ];

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }
}

class Contact extends ActiveRecord {
    protected array $relations = [
        'user' => [ self::BELONGS_TO, User::class, 'user_id' ],
        'user_with_backref' => [ self::BELONGS_TO, User::class, 'user_id', [], 'contact' ],
    ];
    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'contacts');
    }
}

これで参照がセットアップされたので、非常に簡単に使用できます!

$user = new User($pdo_connection);

// 最新のユーザーを検索します。
$user->notNull('id')->orderBy('id desc')->find();

// リレーションを使用して連絡先を取得します:
foreach($user->contacts as $contact) {
    echo $contact->id;
}

// または逆の方法を採用できます。
$contact = new Contact();

// 1 つの連絡先を取得します
$contact->find();

// リレーションを使用してユーザーを取得します:
echo $contact->user->name; // これがユーザー名です

かなりクールですね?

カスタムデータの設定

場合によっては、テンプレートに渡すのが簡単なカスタム計算などが記録にアタッチする必要がある場合があります。

setCustomData(string $field, mixed $value)

setCustomData() メソッドを使ってカスタムデータをアタッチします。

$user->setCustomData('page_view_count', $page_view_count);

そして、通常のオブジェクトプロパティと同様に参照します。

echo $user->page_view_count;

イベント

このライブラリのもう 1 つの素晴らしい機能はイベントについてです。イベントは、特定のメソッドを呼び出すと特定のタイミングでトリガーされます。データを自動的に設定するのに非常に役立ちます。

onConstruct(ActiveRecord $ActiveRecord, array &config)

これは、デフォルトの接続などを設定する必要がある場合に非常に役立ちます。

// index.php または bootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);

//
//
//

// User.php
class User extends flight\ActiveRecord {

    protected function onConstruct(self $self, array &$config) { // $config に & 参照を忘れないでください
        // 自動的に接続を設定する場合
        $config['connection'] = Flight::db();
        // またはこれ
        $self->transformAndPersistConnection(Flight::db());

        // この方法でテーブル名を設定できます
        $config['table'] = 'users';
    } 
}

beforeFind(ActiveRecord $ActiveRecord)

クエリ manipulation が必要な場合にのみ役立ちます。

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeFind(self $self) {
        // 常に id >= 0 を実行する
        $self->gte('id', 0); 
    } 
}

afterFind(ActiveRecord $ActiveRecord)

これは、通常、レコードが取得されるたびに特定のロジックを実行する必要がある場合により役立ちます。何かを複合する必要があるでしょうか?ユーザーに計算件数のクエリを毎回実行する必要がありますか(パフォーマンス的には良くありませんが、そうとわります)?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterFind(self $self) {
        // ソルトを復号化している
        $self->secret = yourDecryptFunction($self->secret, $some_key);

        // おそらくクエリのようなものをカスタムデータに保存中ですか?
        $self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count']; 
    } 
}

beforeFindAll(ActiveRecord $ActiveRecord)

クエリ manipulation が必要な場合にのみ役立ちます。

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeFindAll(self $self) {
        // 常に id >= 0 を実行する
        $self->gte('id', 0); 
    } 
}

afterFindAll(array<int,ActiveRecord> $results)

afterFind() と似ていますが、すべてのレコードにそれを適用できます!

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterFindAll(array $results) {

        foreach($results as $self) {
            // afterFind() のようなことを行います
        }
    } 
}

beforeInsert(ActiveRecord $ActiveRecord)

通常、何らかのデフォルト値を設定する場合に役立ちます。

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeInsert(self $self) {
        // いくつかのデフォルト値を設定する
        if(!$self->created_date) {
            $self->created_date = gmdate('Y-m-d');
        }

        if(!$self->password) {
            $self->password = password_hash((string) microtime(true));
        }
    } 
}

afterInsert(ActiveRecord $ActiveRecord)

挿入後にデータを変更するユースケースがありますか?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterInsert(self $self) {
        // します。
        Flight::cache()->set('most_recent_insert_id', $self->id);
        // またはその他....
    } 
}

beforeUpdate(ActiveRecord $ActiveRecord)

更新時にデフォルト値を設定する必要がある場合に役立ちます。

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeInsert(self $self) {
        // いくつかのデフォルト値を設定する
        if(!$self->updated_date) {
            $self->updated_date = gmdate('Y-m-d');
        }
    } 
}

afterUpdate(ActiveRecord $ActiveRecord)

更新後にデータを変更するユースケースがありますか?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterInsert(self $self) {
        // します。
        Flight::cache()->set('most_recently_updated_user_id', $self->id);
        // またはその他....
    } 
}

beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)

挿入または更新が発生する際にイベントが発生する場合に有用です。説明は省略しますが、何を行うかはお分かりかと思います。

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeSave(self $self) {
        $self->last_updated = gmdate('Y-m-d H:i:s');
    } 
}

beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)

ここで何をしたいかはわかりませんが、ここでは判断はしません!やりたいことを遂行してください!

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeDelete(self $self) {
        echo '彼は勇敢な兵士でした... :cry-face:';
    } 
}

データベース接続管理

このライブラリを使用する際、データベース接続をいくつかの異なる方法で設定できます。接続をコンストラクタで設定するか、config 変数 $config['connection'] を使用するか、setDatabaseConnection()(v0.4.1)を使用できます。

$pdo_connection = new PDO('sqlite:test.db'); // 例
$user = new User($pdo_connection);
// または
$user = new User(null, [ 'connection' => $pdo_connection ]);
// または
$user = new User();
$user->setDatabaseConnection($pdo_connection);

例えば長時間実行される CLI スクリプトを実行していて、定期的に接続を更新する必要がある場合は、$your_record->setDatabaseConnection($pdo_connection) で接続を再設定できます。

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->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ファイルの「内部」であることがラテに伝えられます -->
{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' => 'ホームページ'
        ]);
    }
}

ラテを最大限に活用する方法の詳細については、ラテのドキュメントを参照してください!

Awesome-plugins/awesome_plugins

素晴らしいプラグイン

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

キャッシング

キャッシングはアプリケーションの速度を上げるための素晴らしい方法です。Flightと一緒に使用できるいくつかのキャッシングライブラリがあります。

クッキー

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

デバッグ

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

データベース

データベースはほとんどのアプリケーションの中核です。これがデータの格納と取得方法です。一部のデータベースライブラリは、単にクエリを書き込むためのラッパーであり、一部は完全なORMです。

暗号化

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

セッション

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

テンプレート

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

貢献

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

Examples

クイックスタートが必要ですか?

flightphp/skeleton リポジトリで始めましょう!これは、アプリケーションを実行するのに必要なすべてのものが含まれた単一のページファイルを含むプロジェクトです。また、コントローラーやビューを備えたより充実した例も含まれています。

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

これらはFlightチームによって公式にスポンサーされていませんが、Flightで構築されたプロジェクトの構造化方法についてのアイデアを提供するかもしれません!

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

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