【実務・中級編】レガシーPHPの可変変数($$var)と動的メソッド呼び出しの根絶:静的ディスパッチへの移行 – Hack言語 コア・静的型システムとHHVMのアーキテクチャ解析バイブル

静的解析の「聖域」を守れ:レガシーPHPの動的コードをHackで殲滅する極意

HHVMのアーキテクチャを理解している諸君なら知っているはずだ。我々がHackを設計した理由は、PHPの「自由」という名の「無秩序」を排除し、コンパイル時に実行時の悲劇を未然に防ぐためだ。

`$$var` や `$obj->$method()` といった動的ディスパッチは、HHVMのJITコンパイラにとって悪夢以外の何物でもない。型推論のグラフを寸断し、最適化のチャンスを奪い、何より「実行してみるまでバグがわからない」という、エンジニアの尊厳を傷つけるコードの温床だ。

今日は、レガシーの負債を静的ディスパッチという「純粋な秩序」へ昇華させるための、実務的な解法を伝授する。

—

1. なぜ「動的ディスパッチ」がシステムを殺すのか

HHVMの型チェッカー(`hh_client`)がコードを解析する際、最も恐れるのは「正体不明の参照」だ。

  • 型推論の停止: `$$var` が出現した瞬間、チェッカーは変数の内容を追跡できなくなり、その周辺の型安全性は全て `mixed` に退化する。
  • JITの無効化: HHVMは型が確定しているコードに対して驚異的な最適化を行う。動的な呼び出しは、JITによるインライン化を阻害し、実行時のオーバーヘッドを増大させる。

これらを排除し、コンパイラに「全ての未来」を見せる設計へ移行しなければならない。

—

2. 禁断の「$$var」をEnum駆動ディスパッチャへ置換する

まず、レガシーコードによくある「文字列で変数を操作する」パターンを葬り去ろう。

悪い例(レガシーな混沌)

// 何が入っているかコンパイラには一生わからない
function apply_config(string $key, mixed $value): void {
global $settings;
$settings->{$key} = $value; // 悪夢。型チェックが機能しない。
}

改善策:Enumによる静的マッピング

可能な限り `Enum` を活用し、取りうる値を限定する。これが「型安全なディスパッチ」の第一歩だ。

enum ConfigKey: string {
TIMEOUT = ‘timeout’;
RETRY_LIMIT = ‘retry_limit’;
}

final class Settings {
private int $timeout = 30;
private int $retryLimit = 3;

public function set(ConfigKey $key, int $value): void {
// コンパイラはここで全ての分岐を静的に追跡できる
switch ($key) {
case ConfigKey::TIMEOUT: $this->timeout = $value; break;
case ConfigKey::RETRY_LIMIT: $this->retryLimit = $value; break;
}
}
}

—

3. 「$obj->$method()」をファーストクラス関数で殺す

動的メソッド呼び出しは、多くの場合「状態に応じた処理の切り替え」に使われる。これを `dict` や `vec` に格納された「関数(クロージャ)」として扱うのがHack流だ。

改善策:関数マップ(Strategy Patternの簡略版)

メソッド名ではなく、実行すべきロジックそのものを型として持ち運ぶ。

type ActionHandler = (int) -> void;

final class TaskExecutor {
// 関数を値として保持する(ファーストクラス関数)
private dict $handlers = dict[
‘process’ => (int $id) ==> { / 処理A / },
‘delete’ => (int $id) ==> { / 処理B / },
];

public function run(string $action, int $id): void {
$handler = $this->handlers[$action] ?? null;

if ($handler === null) {
throw new InvalidArgumentException(“未定義のアクション: $action”);
}

// 型安全に呼び出される
$handler($id);
}
}

—

4. 実務で「動的」に頼らざるを得ない時への処方箋

どうしても外部データ(JSON等)に基づいて動的に処理を決定しなければならない場合があるだろう。その時こそ、HSL (Hack Standard Library) の `Shapes` と `Dict` を使い、境界線で「型を強制」する。

境界型(Boundary Typing)の設計

システム内部へデータが入ってくる瞬間に、`Shapes::keyExists` や `is_string` 等でチェックし、`Typed Object` へマッピングしてしまえ。

// 外部からの入力を即座に内部モデルへ変換
function toAction(mixed $rawAction): ActionKey {
return match ($rawAction) {
‘process’ => ActionKey::PROCESS,
‘delete’ => ActionKey::DELETE,
default => throw new Exception(“不正なアクション”),
};
}

—

チーフアーキテクトからの提言

君たちが書くコードは、単なる「動くもの」であってはならない。コンパイラがその意図を理解し、最適化という名の手助けをしてくれる「協力的なコード」であるべきだ。

1. `$$var` を見たら即座にコードレビューを差し戻せ。 それは設計の怠慢だ。
2. `mixed` を撲滅せよ。 型がないということは、責任がないということだ。
3. HSLを活用せよ。 HSLは我々が長年かけて練り上げた、最も効率的で型安全な道具箱だ。

コードを静的に書き換えることは、最初は面倒に感じるかもしれない。だが、実行時に「なぜか落ちる」という深夜のデバッグから君を解放し、システムに鉄壁の堅牢性を与えるのは、この静的解析という名の盾だけだ。

さあ、レガシーを葬り、Hack本来のパフォーマンスを引き出そうじゃないか。

タイトルとURLをコピーしました