依存の「闇」を型で焼き払え:PHPグローバル状態からの脱却とHack的DIの流儀
Hackのコードベースを触るということは、単に「型を書く」ことではない。HHVMのJITコンパイラが吐き出す機械語の最適化効率を最大化し、静的解析が全コードパスを掌握できる「予測可能な状態」へとコードを昇華させることだ。
多くの者がPHPから移行する際、最大の足枷となるのがグローバル変数への依存だ。`$_SESSION`や`$GLOBALS`、あるいはシングルトンによる「どこからでも触れる状態」は、型チェッカー(HHVM Typechecker)の視点では「推論不能なブラックボックス」であり、バグの温床でしかない。
今日は、この「負の遺産」をHackの厳格な型システムでどう解体し、堅牢なDI(Dependency Injection)パターンへと昇華させるか、その極意を伝授する。
—
1. なぜグローバル変数はHHVMを殺すのか
パフォーマンスの観点から言えば、グローバル変数はメモリ上の静的な領域を汚染し、HHVMのプロファイラによる型推論の妨げとなる。それ以上に致命的なのは、「テストの実行順序に依存する脆弱性」を生むことだ。
`public static`な状態管理やグローバル変数は、型システムから見れば「いつでも何が代入されるか分からない危うい箱」に過ぎない。これをHackでは「コンストラクタによる明示的な型付き注入」へと変換する。
—
2. 実践:グローバル依存をDIへ変換するパターン
例えば、ユーザーのセッション情報を直接読み取るレガシーなコードを、インターフェースを介した注入型に変換してみよう。
悪い例(PHP的・グローバル汚染)
// 何が起きるか予測不能。単体テストが困難。
function get_user_data(): mixed {
return $_SESSION[‘user_info’] ?? null;
}
良い例(Hack的・DIによる注入)
まずは、依存関係を抽象化するインターフェースを定義し、それを型安全に注入する。
namespace App\Infrastructure;
// ユーザー情報の取得を抽象化
interface IUserSession {
public function getUserId(): ?int;
}
// 実際の実装(HHVMのTypecheckerが厳密にチェックする)
final class SessionManager implements IUserSession {
public function getUserId(): ?int {
// HSL (Hack Standard Library) を使用して安全にアクセス
return $_SESSION[‘uid’] as ?int;
}
}
次に、この依存をクラスのコンストラクタへ「注入」する。
namespace App\Service;
use App\Infrastructure\IUserSession;
final class UserProfileService {
// コンストラクタで型を明示。これにより依存関係が可視化される
public function __construct(
private IUserSession $session,
) {}
public function getProfile(): string {
$uid = $this->session->getUserId();
if ($uid === null) {
throw new \Exception(“Unauthorized”);
}
return “User Profile: ” . (string)$uid;
}
}
—
3. HHVMアーキテクチャを活かす設計上の注意点
この設計には、単なる「行儀の良さ」以上のメリットがある。
1. 静的解析の完全性: `UserProfileService`をインスタンス化する際、HHVMの型チェッカーは`IUserSession`の実装が確実に渡されることを保証する。実行時に「キーがない」というエラーは発生し得ない。
2. JIT最適化の向上: HHVMのJITは、具象クラスがコンストラクタで確定している場合、メソッド呼び出しのインライン化を極限まで進めることができる。グローバル変数アクセスのような「遅延解決」を排除することで、実行速度は劇的に向上する。
3. モックの容易さ: テスト時には`IUserSession`のモックを渡すだけで済む。グローバル変数の書き換えといったトリッキーな手法は一切不要だ。
—
4. プロダクションへの適用:DIコンテナの導入
実務では、いちいち手動でインスタンスを作るのは非効率だ。だが、過剰なDIコンテナの多機能化は、Hackの「静的解析」の恩恵を損なう。
推奨戦略:
- オートワイアリングを過信しない: 複雑なReflectionを使うよりも、`Composition Root`(アプリケーションの起動直下)で依存を明示的に組み立てる。
- `shape`型の活用: HSLの`dict`や`shape`を使って、設定値は型付きで注入する。
// コンポジションルートの例
final class AppContainer {
public static function create(): UserProfileService {
// 依存関係をここで明確に結びつける
$session = new SessionManager();
return new UserProfileService($session);
}
}
—
結び:コードは「型」で語らせろ
Hackの型システムは、開発者を縛る鎖ではない。それは「あなたの書くコードが、実行時に決して裏切らないこと」を証明するための強力な武器だ。
グローバル変数に頼る開発は、砂上の楼閣を建てるようなものだ。しかし、このDIパターンへの移行は、アプリケーションを強固な鉄骨構造へと作り替える。
コードレビューでこの設計を見たら、自信を持って承認してほしい。なぜなら、そのコードはHHVMのアーキテクチャのポテンシャルを最大限に引き出し、未来のバグを未然に防ぐ「防弾設計」になっているはずだからだ。
さあ、レガシーを解体し、型で世界を掌握せよ。