コーディングガイド

プロジェクト

vendorは会社の名前やチームの名前または個人の名前(excite,koriym等)を指定して、packageにはアプリケーション(サービス)の名前(blog, news等)を指定します。 プロジェクトはアプリケーション単位で作成し、Web APIとHTMLを別ホストでサービスする場合でも1つのプロジェクトにします。

スタイル

PSR1, PSR2, PSR4に準拠します。

<?php
namespace Koriym\Blog\Resource\App;

use BEAR\RepositoryModule\Annotation\Cacheable;
use BEAR\Resource\Annotation\Embed;
use BEAR\Resource\Annotation\Link;
use BEAR\Resource\Code;
use BEAR\Resource\ResourceObject;

#[CacheableResponse]
class Entry extends ResourceObject
{
    public function __construct(
        private readonly ExtendPdoInterface $pdo,
        private readonly ResourceInterface $resource
    ) {}

    #[Embed(rel: "author", src: "/author{?author_id}")]
    public function onGet(string $author_id, string $slug): static
    {
        // ...

        return $this;
    }

    #[Link(rel: "next_action1", href: "/next_action1")]
    public function onPost (
        string $tile,
        string $body,
        string $uid,
        string $slug
    ): static {
        // ...
        $this->code = Code::CREATED;

        return $this;
    }
}

リソースのdocBlockコメントはオプションです。リソースURIや引数名だけで説明不十分な時にメソッドの要約(一行)、説明(複数行可)、@paramsを付加します。

/**
 * A summary informing the user what the associated element does.
 *
 * A *description*, that can span multiple lines, to go _in-depth_ into the details of this element
 * and to provide some background information or textual references.
 *
 * @param string $arg1 *description*
 * @param string $arg2 *description*
*/

リソース

リソースについてのベストプラクティスはリソースのベストプラクティスをご覧ください。

グローバル

グローバルな値をリソースやアプリケーションのクラスで参照することは推奨されません。(Modulesでのみ使用します)

  • スーパーグローバルの値を参照しない
  • defineは使用しない
  • 設定値を保持するConfigクラスを作成しない
  • グローバルなオブジェクトコンテナ(サービスロケータ)を使用しない [1], [2]
  • date関数やDateTimeクラスで現在時刻を直接取得することは推奨されません。外部から時刻をインジェクトします。1
  • スタティックメソッドなどのグローバルなメソッドコールも推奨されません。

  • アプリケーションコードが必要とする値は設定ファイルなどから取得するのではなく、全てインジェクトします。2

クラスとオブジェクト

  • トレイトは推奨されません。3
  • 親クラスのメソッドを子クラスが使うことは推奨されません。共通する機能は継承やtraitで共有ではなくクラスにしてインジェクトして使います。継承より合成します。

スクリプトコマンド

  • composer setupコマンドでアプリケーションのセットアップが完了することが推奨されます。このスクリプトではデータベースの初期化、必要ライブラリの確認が含まれます。.envの設定などマニュアルな操作が必要な場合はその手順が画面表示されることが推奨されます。

リソース

  • onPostで作成したリソースのURIはLocationヘッダーで示します。
public function onPost(string $title): static
{
    // ...
    $this->code = 201;
    $this->headers['Location'] = "/task?id={$id}";

    return $this;
}

リソースのベストプラクティスはリソースベストプラクティスもご覧ください。

コード

適切なステータスコードを返します。テストが容易になり、botやクローラーにも正しい情報が伝えることができます。

  • 100 Continue 複数のリクエストの継続
  • 200 OK
  • 201 Created リソース作成
  • 202 Accepted キュー/バッチ 受付
  • 204 No Content bodyがない場合
  • 304 Not Modified 未更新
  • 400 Bad Request リクエストに不備
  • 401 Unauthorized 認証が必要
  • 403 Forbidden 禁止
  • 404 Not Found
  • 405 Method Not Allowed
  • 503 Service Unavailable サーバーサイドでの一時的エラー

304#[Cacheable]アトリビュートを使っていると自動設定されます。404はリソースクラスがない場合、405はリソースのメソッドがない場合に自動設定されます。またDBの接続エラーなどは必ず503で返しクローラーに伝えます。[1]

HTMLのFormメソッド

BEAR.SundayはHTMLのWebフォームでPOSTリクエストの時にX-HTTP-Method-Overrideヘッダーや_methodクエリーを用いてメソッドを上書きする事ができますが、推奨しているわけではありません。PageリソースではonGetonPost以外を実装しない方針でも問題ありません。[1],[2]

ハイパーリンク

  • リンクを持つリソースは#[Link]で示すことが推奨されます。
  • リソースは意味のまとまりのグラフにして#[Embed]で埋め込む事が推奨されます。

DI

  • 実行コンテキスト(prod, devなど)の値そのものをインジェクトしてはいけません。代わりにコンテキストに応じたインスタンスをインジェクトします。アプリケーションはどのコンテキストで動作しているのか無知にします。
  • ライブラリコードではセッターインジェクションは推奨されません。
  • Provider束縛を可能な限り避けtoConstructor束縛を優先することが推奨されます。
  • Moduleで条件に応じて束縛をすることを避けます。 (AvoidConditionalLogicInModules)
  • モジュールのconfigure()から環境変数を参照しないで、コンストラクタインジェクションにします。

AOP

  • インターセプターの適用を必須にしてはいけません。例えばログやDBのトランザクションなどはインターセプターの有無でプログラムの本質的な動作は変わりません。
  • メソッド内の依存をインターセプターがインジェクトしないようにします。メソッド実装時にしか決定できない値は@Assistedインジェクションで引数にインジェクトします。
  • 複数のインタセプターがある場合にその実行順に可能な限り依存しないようにします。
  • 無条件に全メソッドに適用するインターセプターであればbootstrap.phpでの記述を考慮してください。
  • 横断的関心事と、本質的関心事を分けるために使われるものです。特定のメソッドのhackのためにインターセプトするような使い方は推奨されません。

環境

  • Webだけでしか動作しないアプリケーションは推奨されません。テスト可能にするためにコンソールでも動作するようにします。
  • .envファイルをプロジェクトリポジトリに含まない事が推奨されます。
  • .envの代わりにスキーマを記述するKoriym.EnvJsonの利用を検討してください。

テスト

  • リソースクライアントを使ったリソーステストを中心にし、必要があればリソースの表現のテスト(HTMLなど)を加えます。
  • ハイパーメディアテストはユースケースをテストとして残すことができます。
  • prodはプロダクション用のコンテキストです。テストでprodコンテキストの利用は最低限、できれば無しにしましょう。

HTMLテンプレート

  • 大きなループ文を避けます。ループの中のif文はジェネレーター で置き換えれないか検討しましょう。

  1. koriym/now 

  2. Web APIなど外部のシステムの値を利用する時には、クライアントクラスやWeb APIアクセスリソースなど1つにの場所に集中させDIやAOPでモッキングが容易にするようにします。 

  3. ResourceInjectなどのインジェクション用トレイトはインジェクションのボイラープレートコードを削減するために存在しましたが、PHP8で追加されたコンストラクタの引数をプロパティへ昇格させる機能により意味を失いました。コンストラクタインジェクションを使いましょう。