[PHP]token_nameとは?トークンIDを人間が読める名前に変換する方法を徹底解説

PHP

はじめに

前回の記事では token_get_all() を使ってPHPソースコードをトークンの配列に分解する方法を紹介しました。その中で、トークンは [トークンID, テキスト, 行番号] という形の配列で返ってくることを説明しましたが、この「トークンID」はただの整数(内部的には T_ECHOT_VARIABLE のような定数)であり、そのままでは人間にとって読みにくいものです。

そこで使うのが token_name() 関数です。この関数はトークンIDを受け取り、"T_ECHO""T_VARIABLE" のような、定数名そのものを表す文字列を返してくれます。デバッグ出力やログ、解析結果のレポート作成など、トークンを人間が理解できる形で表示したいときに欠かせない関数です。本記事では基本的な使い方から実践的な活用例まで詳しく解説します。


関数概要

項目内容
関数名token_name()
所属拡張Tokenizer(標準で有効)
シグネチャtoken_name(int $id): string
引数$id — トークンID(T_ECHO などのPHP定数、または対応する整数値)
戻り値トークンIDに対応する定数名を表す文字列(例: "T_ECHO"
対応バージョンPHP 4以降
OOP版の有無あり。PHP 8.0以降は PhpToken::getTokenName() インスタンスメソッドが利用可能
未知のIDの場合対応する定数が見つからない場合は "UNKNOWN" を返す

処理の流れ(イメージ図)

  token_get_all() の結果
  ┌───────────────────────────────┐
  │ [T_ECHO(定数=317など), "echo", 1] │
  └───────────────────────────────┘
              │
              │ トークンIDを取り出す(整数)
              ▼
        token_name(317)
              │
              │ 内部テーブルを引いて名前に変換
              ▼
          "T_ECHO"  ← 文字列として返る

ポイントは、token_get_all() が返すトークンIDは単なる整数であり、token_name() を介さないと、それが具体的に何を意味するトークンなのか人間にはわからないという点です。両者はセットで使われることが非常に多い関数です。


実践サンプル7選

例1:基本的な使い方

<?php

class BasicTokenNamer
{
    public function describe(int $tokenId): string
    {
        // token_name() は整数IDを受け取り、定数名の文字列を返す
        return token_name($tokenId);
    }
}

$namer = new BasicTokenNamer();
echo $namer->describe(T_ECHO) . PHP_EOL;      // "T_ECHO"
echo $namer->describe(T_VARIABLE) . PHP_EOL;   // "T_VARIABLE"
echo $namer->describe(T_STRING) . PHP_EOL;     // "T_STRING"

例2:token_get_allの結果を読みやすく整形するダンプツール

<?php

class TokenDumper
{
    /**
     * token_get_all() の結果を「名前 => テキスト」の形式で一覧表示する
     */
    public function dump(string $code): array
    {
        $tokens = token_get_all($code);
        $result = [];

        foreach ($tokens as $token) {
            if (is_array($token)) {
                // token_name() でIDを名前に変換してから格納
                $result[] = token_name($token[0]) . ' => ' . var_export($token[1], true);
            } else {
                $result[] = "'{$token}' (単一文字トークン)";
            }
        }

        return $result;
    }
}

$dumper = new TokenDumper();
print_r($dumper->dump('<?php $x = 1;'));

例3:特定カテゴリのトークン名をフィルタリングする

<?php

class TokenCategoryFilter
{
    /**
     * トークン名が指定した接頭辞(例: "T_")で始まるものだけを抽出する
     * ここでは特にコメント系トークンのみを抽出する例
     */
    public function filterComments(string $code): array
    {
        $tokens = token_get_all($code);
        $comments = [];

        foreach ($tokens as $token) {
            if (!is_array($token)) {
                continue;
            }

            $name = token_name($token[0]);
            if (in_array($name, ['T_COMMENT', 'T_DOC_COMMENT'], true)) {
                $comments[] = $token[1];
            }
        }

        return $comments;
    }
}

$filter = new TokenCategoryFilter();
$code = "<?php\n// 単行コメント\n/** ドキュメントコメント */\n\$x = 1;\n";
print_r($filter->filterComments($code));

例4:トークンID一覧から名前の対応表を生成する

<?php

class TokenTableBuilder
{
    /**
     * よく使われる代表的なトークン定数について
     * ID => 名前 の対応表を作成する
     */
    public function build(array $tokenIds): array
    {
        $table = [];
        foreach ($tokenIds as $id) {
            // 定数値そのものをキーにして名前を対応付ける
            $table[$id] = token_name($id);
        }
        return $table;
    }
}

$builder = new TokenTableBuilder();
$ids = [T_ECHO, T_IF, T_ELSE, T_WHILE, T_FUNCTION, T_CLASS];
print_r($builder->build($ids));

例5:未知のトークンIDを安全に扱う

<?php

class SafeTokenNamer
{
    /**
     * 不正な、または存在しないトークンIDが渡された場合に
     * "UNKNOWN" が返ることを利用して安全に処理する
     */
    public function nameOrDefault(int $tokenId, string $default = '不明なトークン'): string
    {
        $name = token_name($tokenId);

        if ($name === 'UNKNOWN') {
            return $default;
        }

        return $name;
    }
}

$namer = new SafeTokenNamer();
echo $namer->nameOrDefault(T_ECHO) . PHP_EOL;      // "T_ECHO"
echo $namer->nameOrDefault(999999) . PHP_EOL;      // "不明なトークン"

例6:構文解析ログを生成するシンプルなツール

<?php

class SyntaxLogger
{
    /**
     * トークンごとに「行番号: トークン名 (テキスト)」形式のログ行を生成する
     */
    public function generateLog(string $code): array
    {
        $tokens = token_get_all($code);
        $log = [];

        foreach ($tokens as $token) {
            if (is_array($token)) {
                [$id, $text, $line] = $token;
                $log[] = sprintf('%d行目: %s (%s)', $line, token_name($id), trim($text));
            }
        }

        return $log;
    }
}

$logger = new SyntaxLogger();
$code = "<?php\nif (\$flag) {\n    echo 'ok';\n}\n";
foreach ($logger->generateLog($code) as $line) {
    echo $line . PHP_EOL;
}

例7:PhpToken::getTokenName()を使ったOOP版(PHP 8.0以降)

<?php

class OopTokenNamer
{
    /**
     * PHP 8.0以降ではPhpTokenインスタンスの
     * getTokenName()メソッドで同様の情報を取得できる
     */
    public function describeAll(string $code): array
    {
        $tokens = PhpToken::tokenize($code);
        $result = [];

        foreach ($tokens as $token) {
            // token_name($token->id) と実質同じ結果が得られる
            $result[] = $token->getTokenName() . ' => ' . var_export($token->text, true);
        }

        return $result;
    }
}

$namer = new OopTokenNamer();
print_r($namer->describeAll('<?php $x = 1;'));

関連関数との比較

関数/クラス役割token_nameとの違い
token_name()トークンIDを定数名の文字列に変換本記事の対象。手続き型の変換関数
token_get_all()ソースコードをトークン配列に変換token_name() に渡すIDの供給元となる関数
PhpToken::getTokenName()同上(OOP版、PHP 8.0以降)PhpToken インスタンスに対して呼び出すメソッド版
PhpToken::tokenize()ソースコードを PhpToken オブジェクトの配列に変換token_get_all() のOOP版であり、getTokenName() と組み合わせて使う
get_defined_constants()定義済み定数一覧の取得T_* 定数がどんな値を持つか確認する際の補助として使えることがある

よくある落とし穴(注意点)

  1. 単一文字トークンにはIDが存在しない token_get_all() が返す単一文字トークン(;( など)は文字列そのものであり、トークンIDを持ちません。token_name() に渡す前に、必ず is_array() で配列トークンかどうかを確認する必要があります。
  2. 未知のIDには”UNKNOWN”が返る 存在しない、または不正な整数を渡した場合、例外やエラーではなく "UNKNOWN" という文字列が返ります。これに気づかずログ出力すると原因調査に時間がかかることがあります。
  3. トークン名とトークンIDを混同しない token_name() の戻り値はあくまで表示用の文字列であり、switch 文などでの比較には使わず、T_ECHO のような定数そのものを使うべきです(文字列比較よりも定数比較の方が安全かつ高速です)。
  4. PHPのバージョンによってトークンの種類が増減する 新しい構文(例: match式や enum など)が追加されると、対応するトークン定数も新設されます。古いPHPバージョンでは存在しないトークン名に遭遇することがあるため、バージョン差異には注意が必要です。
  5. パフォーマンスを気にする場合はOOP版も検討 大量のトークンに対して token_name() を都度呼び出すよりも、PHP 8.0以降であれば PhpToken オブジェクトの getTokenName() を使う方が、コードの見通しが良くなる場合があります。

まとめ

観点まとめ
何をする関数かトークンID(整数)を人間が読める定数名の文字列に変換する
主な用途token_get_all() の結果をデバッグ表示・ログ出力・レポート化する際の補助
戻り値の形式定数名を表す文字列(未知のIDの場合は”UNKNOWN”)
OOP代替PHP 8.0以降は PhpToken::getTokenName() インスタンスメソッドが利用可能
注意点単一文字トークンにはID自体が存在しない点、UNKNOWNの扱い、比較には定数を使うこと

token_name() は単体で使うことは少ないものの、token_get_all()PhpToken と組み合わせることで、PHPコードの解析結果を人間にとって読みやすい形に変換できる、地味ながら欠かせない存在です。ぜひ前回の token_get_all() の記事と合わせて活用してみてください。

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