境界線上のサバイバル:PartialからStrictへの移行における「型安全性の防波堤」
Hackのアーキテクチャを理解している諸君なら、`hh_client`が単なる静的解析ツールではなく、HHVMのJITコンパイルにおける最適化エンジンと対をなす「信頼の根幹」であることを知っているはずだ。
大規模なレガシーコードベースを抱えるプロジェクトにおいて、`<<__Strict>>`への全移行は理想だが、現実はそう甘くない。Partialモード(`1. 境界線(Boundary)の設計:防御的プログラミングの真髄
PartialなコンテキストからStrictなコンテキストへ値を渡すとき、我々は「地雷」を渡している可能性がある。特にコレクションやNullableな値は、HHVMのランタイムにおいて最適化の足枷となり、型チェッカーが追跡不能な `mixed` の温床となる。
アンチパターン:境界での安易なキャスト
// Partialなコードから受け取ったデータをそのまま使うのは自殺行為だ
function process_data(mixed $raw_data): void {
// ここで is_array() 等でチェックしても、型推論は汚染されたままだ
$data = (array)$raw_data;
// 構造が保証されていない配列をループで回すのは、将来の障害を予約しているに等しい
}
解決策:DTO(Data Transfer Object)による「型変換ゲート」
境界線で一度、`shape` または `record` に変換し、型を確定させる。これにより、Strictモード側は「信頼できるデータ」のみを扱うことが可能になる。
<<__ConsistentConstruct>>
abstract class DataBoundary {
// 外部からの入力を厳格に型定義されたShapeへ変換する
abstract public static function fromMixed(mixed $input): shape(‘id’ => int, ‘name’ => string);
}
—
2. 「Partialの汚染」を遮断するインターフェース設計
Partialモードのコードには `dynamic` が紛れ込む。これがStrictモードへ流入すると、型チェッカーの推論が「諦め」の境地に達し、`TAny`(未知の型)が蔓延する。これを防ぐには、インターフェースによる疎結合化と、`readonly` 修飾子の活用が不可欠だ。
// Strictモードのコンポーネント定義
interface IService {
// 入力に mixed を許容してはならない。必ず型を強制する
public function execute(string $id): Awaitable
}
// 実装クラスでは、境界での検証を責務とする
final class UserServiceImpl implements IService {
public async function execute(string $id): Awaitable
// 外部APIやレガシーDBからの応答はここでShape検証を行う
$raw = await fetch_from_legacy_api($id);
// 型安全でないデータをここで排除する
return User::fromShape($this->validate($raw));
}
}
—
3. パフォーマンスへの影響:型チェックとHHVMの最適化
よくある誤解だが、「型チェックを厳しくすると実行速度が落ちる」ことはない。むしろ逆だ。`Strict`モードで型が明確であればあるほど、HHVMのJITコンパイラは `TypeGuard` の挿入を最小限に抑え、ネイティブコードに近い最適化を行える。
逆に、`Partial`モードの `mixed` だらけのコードは、HHVMが実行時に「この変数は結局何型なんだ?」というチェックを繰り返すため、確実にCPUサイクルを浪費する。
最適化の要諦:
- `vec` や `dict` の初期化を型推論に頼らない: 可能な限り明示的な型指定を行い、HHVMが型情報をキャッシュしやすい構造を作る。
- `tuple` を駆使する: 構造が固定されている場合は、`shape`よりも`tuple`の方がメモリ効率とパース速度において優位な場合が多い。
—
4. 現場で使える「型安全性の防波堤」実装例
最後に、レガシーな `array` をStrictな世界に持ち込む際の、最も安全なパターンを提示する。
/
final class UserFactory {
type TUserShape = shape(‘id’ => int, ‘email’ => string);
public static function fromLegacyArray(mixed $data): this::TUserShape {
if (!is_dict($data)) {
throw new InvalidArgumentException(‘Dict expected’);
}
// ここで明示的に型を確定させる
return shape(
‘id’ => (int)($data[‘user_id’] ?? 0),
‘email’ => (string)($data[‘email’] ?? ‘unknown’),
);
}
}
// 呼び出し側
async function run(): Awaitable
$legacy_data = get_legacy_data(); // mixed を返すレガシー関数
$user = UserFactory::fromLegacyArray($legacy_data);
// 以降、$user は完全な型推論の恩恵を受けられる
echo $user[‘email’];
}
結び:技術的負債は「型」で切り離せ
大規模リファクタリングにおいて、すべてを一度に移行しようとするのは無謀だ。重要なのは、「どこが型安全で、どこが未定義か」という境界線を明確に引くことにある。
Strictモードへの移行とは、単なる文法の強制ではない。それは、君たちのコードベースに「信頼の契約」を導入する作業だ。型チェッカーが `No errors` を出したとき、それは単にエラーがないことを意味するのではない。君たちが書いたコードが、HHVMという強固な実行基盤の上で、いかなる入力に対しても論理的に破綻しないことを証明した瞬間なのだ。
さあ、エディタを開け。まずは境界線での検証から始めるんだ。それが、真のアーキテクトへの第一歩だ。