Search by

apparel-php / fabric

apparel-php

Fundamental Assortment for Building Runtime-Isolated Contexts

Package info

github.com/apparel-php/fabric

pkg:composer/apparel-php/fabric

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.0.0 2025-08-22 01:58 UTC

This package is not auto-updated.

Last update: 2026-11-10 09:59:31 UTC


README

Fabric は、環境依存の情報 (現在時刻や乱数の取得など) を取り扱うためのシンプルなコンポーネント群です。

このライブラリを使用することで、プログラムの保守性・テスト容易性を向上させることができます。

提供するコンポーネントと基本的な使い方

Fabric は、副作用を伴う代表的な処理である「時刻の取得」と「乱数の生成」を抽象化するインタフェースと、 用途に応じた具象クラスを提供します。

1. Clock (時刻の抽象化)

現在時刻の取得を抽象化する Fabric\Clock インタフェースと、以下の実装を提供しています。

  • DefaultClock: time() 関数を利用して実際のシステム時刻を返す、本番環境用の標準実装です。
  • FixedClock: コンストラクタで指定された固定の Unix Time を常に返します。時刻に依存したテストなどで使用します。
  • ShiftedClock: 基準となる他の Clock から、指定した秒数だけ進ませた (または遅らせた) 時刻を返します。ユーザーのタイムゾーンに合わせた時刻表示に応用することもできます。

使用例:

use Fabric\DefaultClock;
use Fabric\FixedClock;
use Fabric\ShiftedClock;

// 本番用
$clock = DefaultClock::getInstance();
echo $clock->getTime(), PHP_EOL; // 実際の現在時刻

// テスト用 (常に 2025-01-01 00:00:00 UTC のタイムスタンプを返す)
$testClock = new FixedClock(1735689600);
echo $testClock->getTime(), PHP_EOL; // 常に 1735689600

// 基準時間から 1 時間 (3600 秒) 進んだ時間を返す
$shiftedClock = new ShiftedClock(3600, $testClock);
echo $shiftedClock->getTime(), PHP_EOL; // 常に1735693200

2. Random (乱数生成の抽象化)

乱数の生成を抽象化する Fabric\Random インタフェースと、以下の実装を提供しています。

  • DefaultRandom: mt_rand() を利用する高速な標準実装です。暗号学的な安全性は保証されません。
  • SecureRandom: random_int() を利用する、暗号学的に安全な乱数ジェネレータです。トークン生成などのセキュリティ用途で使用します。
  • ArrayRandom: コンストラクタで指定された配列の要素を順番に返します。予測可能な乱数列をテストでシミュレートするために使用します。

使用例:

use Fabric\SecureRandom;
use Fabric\ArrayRandom;

// 本番用 (セキュアな乱数)
$random = SecureRandom::getInstance();
echo $random->next(), PHP_EOL;

// テスト用 (常に 1, 5, 3, 1, 5, 3, ... の順番で出力する)
$testRandom = new ArrayRandom([1, 5, 3]);
echo $testRandom->next(), PHP_EOL; // 1
echo $testRandom->next(), PHP_EOL; // 5

設計思想: Runtime-Isolated (実行時環境の隔離) とは?

なぜ time() や mt_rand() といった PHP の便利な標準関数を直接使わず、わざわざ Clock や Random を経由するのでしょうか? それは、アプリケーションの信頼性 (同じ条件であれば、常に同じ結果を出すこと) を担保するためです。

たとえばプログラム内で直接 time() を呼び出している場合、その処理は「実行するタイミング」 によって結果が変わってしまいます。これを 「環境依存」 (または 「副作用」) と呼びます。 環境依存がハードコードされていると「深夜 0 時をまたいだ瞬間の挙動」や 「特定の乱数が生成された場合のエッジケース」などをテストすることが極めて困難になります。

Runtime-Isolated (実行時環境の隔離) とは、こうした「実行時に変動する外部状態」 をアプリケーションのコアロジックから切り離し、外部から注入 (DI: Dependency Injection) できる形に整える設計アプローチです。

実践的な解説: Runtime-Isolated Context の実現

Fabric を活用して、実行時環境に依存しないテストしやすいプログラムを構築する実践例を見てみましょう。

改善前のコード (密結合・テストが困難)

以下のクラスは内部で直接 time() を呼び出しているため、 期限切れのテストを実行するタイミングによって結果が変わってしまいます。

class Coupon
{
    /** 有効期限切れとなる Unix time */
    private $expiresAt;

    public function __construct(int $expiresAt)
    {
        $this->expiresAt = $expiresAt;
    }

    public function isExpired(): bool
    {
        // NG: 実行時のシステム時刻に依存している
        return time() > $this->expiresAt;
    }
}

Fabric を使った改善 (疎結合・テストが容易)

Fabric\Clock をメソッドの引数 (またはコンストラクタ) から注入するように変更します。 これにより、このクラス自身は「現在時刻が何であるか」を気にする必要がなくなり、単純に 「渡された Clock の時間と期限を比較する」という関数的な振る舞いになります。

use Fabric\Clock;

class Coupon
{
    private $expiresAt;

    public function __construct(int $expiresAt)
    {
        $this->expiresAt = $expiresAt;
    }

    public function isExpired(Clock $clock): bool
    {
        // OK: 環境依存の情報 (Clock) が外部から隔離・注入されている
        return $clock->getTime() > $this->expiresAt;
    }
}

テストにおける恩恵

環境依存の情報を切り離したことで、テスト時には FixedClock を注入するだけで、 あらゆる時間軸のテストを確実かつ高速に実行できるようになります。

use Fabric\FixedClock;

// クーポンの期限を 1000 に設定
$coupon = new Coupon(1000);

// 期限前のテスト
$clockBefore = new FixedClock(999);
assert($coupon->isExpired($clockBefore) === false);

// 期限切れのテスト
$clockAfter = new FixedClock(1001);
assert($coupon->isExpired($clockAfter) === true);

このように Fabric の提供するコンポーネント群をアプリケーションの基盤として採用することで、 実行時環境を隔離した状態 (Runtime-Isolated Context) を作ることができます。

インストール

Composer からインストールできます。

composer require apparel-php/fabric

動作要件: PHP 7.0 以上