こんにちは!HHVMの内部構造やHackの厳格な型システムの世界へようこそ。チーフアーキテクトの私です。
他の言語、例えばPHPやTypeScriptからHackの世界に飛び込んできた開発者の多くが、その圧倒的な速度と「一切の妥協を許さない厳格な静的型システム(Strict Mode)」の美しさに魅了されますよね。
でも、開発現場が進むにつれて、こんな風に思ったことはありませんか?
「標準の型チェッカーだけでは表現しきれない、ウチのプロジェクト固有のビジネスルールやセキュリティ制約を、ビルド時に自動で検知させたい!」 と。
今回は、Hackが持つ強力な機能である「カスタム属性(Attribute)」をフックし、HHVMの型チェッカーの世界を拡張して独自の静的解析を自動化する極意を、優しく、そして深く解説していきますね。ここをクリアすれば、あなたも立派なHackアーキテクトの仲間入りです!
—
1. Hackの型チェッカーと「カスタム属性」の基本
Hackの静的型チェッカー(`hh_client` / `hh_server`)は、コードを実行する前に型エラーをミリ秒単位で検出してくれます。このチェッカーは非常に賢いのですが、標準機能だけでは「このメソッドの引数は、特定のフォーマットをクリアした文字列でなければならない」といった、ドメイン特有の制約まではデフォルトではチェックできません。
そこで登場するのがカスタム属性(Attribute)です。
属性を使うことで、クラスやメソッド、プロパティに「メタデータ」を付与できます。
イメージ図:静的解析のパイプライン
[ あなたのHackコード ]
↓ (カスタム属性が付与されている)
[ hh_client (型チェッカー) ]
↓
[ カスタム静的解析スクリプト (ASTを走査) ]
↓
[ 違反があればビルドを即座にブロック! ]
このように、コードに「印(属性)」をつけておき、それを解析ツールに読み込ませることで、独自のルールを強制させることができるのです。
—
2. 実践:カスタム属性の定義とコードへの適用
まずは、コード側でどのように属性を定義し、利用するのか見ていきましょう。Hackでは、属性は `<<__Rx>>` のようなシステム組み込みのものだけでなく、ユーザーが独自に定義することも可能です。
今回は例として、「センシティブなデータを扱うメソッドには、必ず監査ログの記録先を指定しなければならない」という独自ルールを強制するシステムを作ってみましょう。
実際に書くHackコード
// strict
namespace HackArchitect\Security;
/
- 監査ログの記録先を強制するカスタム属性
- [[__Attribute]] を付与することで、属性として機能させます。
/
<<__Attribute(UserAttribute::TARGET_METHOD)>>
class AuditRequired implements \HH\ClassAttribute {
public function __construct(public string $logDestination) {}
}
class UserProfileService {
// 【OKな例】カスタム属性にログ先(”security_db”)が明示されている
<
public function updatePassword(int $userId, string $newPassword): void {
// パスワード更新のロジック
}
// 【NGな例になりうるメソッド】
// センシティブな処理なのに属性がついていない、または不正な値が入っている場合
public function deleteUserAccount(int $userId): void {
// アカウント削除のロジック(本当は属性が必要!)
}
}
ここがポイント!
- `<<__Attribute(UserAttribute::TARGET_METHOD)>>` により、この属性が「メソッドに対してのみ付与できる」という制約をコンパイラレベルでかけられます。文法ミスを未然に防ぐHackらしいアプローチですね。
—
3. 型チェッカーを拡張する静的解析ツールの設計
属性を貼るだけでは、ただの「飾り」になってしまいます。これを「静的解析のルール」として強制するために、HackのAST(抽象構文木)を走査するカスタムチェッカーをスクリプトとして構築します。
Hackには、コードの構造をJSON形式などで出力する仕組みや、HHVMのライブラリを通じてASTを解析する手段が用意されています。ここでは概念的なアプローチとして、カスタムチェッカーがどのようにコードを検証するかを見てみましょう。
// strict
namespace HackArchitect\Analyzer;
use namespace HH\Lib\{C, Str, Vec};
/
- 独自の静的解析ランナーのイメージ
/
class AuditAttributeAnalyzer {
public static function analyzeCodebase(vec
$hasError = false;
foreach ($classNames as $className) {
$reflector = new \ReflectionClass($className);
foreach ($reflector->getMethods() as $method) {
// メソッド名が ‘delete’ または ‘update’ で始まる重要処理か?
$isSensitive = Str\is_prefix($method->getName(), ‘delete’) ||
Str\is_prefix($method->getName(), ‘update’);
if ($isSensitive) {
// AuditRequired 属性がついているかチェック
$attributes = $method->getAttributes();
if (!C\contains_key($attributes, \HackArchitect\Security\AuditRequired::class)) {
\fprintf(
\STDERR,
“【静点解析エラー】メソッド %s::%s は機密処理ですが、<
$className,
$method->getName()
);
$hasError = true;
}
}
}
}
return $hasError;
}
}
この解析スクリプトをCI/CDパイプライン(GitHub Actionsなど)や、ローカルの `hh_client` のフックとして組み込むことで、「属性の付け忘れ」を人間ではなくマシンが100%確実に検知できるようになります。
—
4. 陥りやすい文法エラーと注意点
Hackでカスタム属性やメタプログラミング的なアプローチを取る際、初心者がハマりやすいポイントをいくつかお伝えしておきますね。
1. Strict Mode (`// strict`) のし忘れ
- Hackの恩恵を最大限に受けるため、解析ツール側もコード側も必ず最上位の厳格モードで記述してください。動的な型混じりのコード片があると、リフレクションやASTの構造が不安定になります。
2. 属性ターゲットの指定ミス
- `<<__Attribute(UserAttribute::TARGET_CLASS)>>` と定義したのに、メソッドに付与してしまった場合、型チェッカーが即座にビルドエラーを吐きます。「あれ、なんで動かないんだろう?」と思ったらまずターゲットのスコープを確認しましょう。
3. 実行時コストの誤解
- 属性やリフレクションは便利ですが、あまりに過剰に実行時(Runtime)の処理に組み込むとHHVMの最適化を阻害する場合があります。「静的解析(ビルド時・CI時)で検証し、実行時は極力クリーンに保つ」のがHackアーキテクトの黄金律です。
—
まとめ
いかがでしたか?今回は「カスタム属性を用いた静的解析の自動化」という、一歩進んだHackの活用術を解説しました。
- カスタム属性でコードに意味のあるメタデータを付与する。
- 型チェッカーや静的解析スクリプトと組み合わせることで、チーム全体のコーディング規約やセキュリティ要件を自動で強制する。
ここをマスターすれば、大規模なコードベースであっても、品質の揺らぎを完全にコントロールできるようになります。
あなたのHackライフが、よりスピーディで、より堅牢なものになることを応援しています。それでは、次のアーキテクチャでお会いしましょう!