[PHP]__LINE__とは?コード中の現在行番号を取得するマジック定数を徹底解説

PHP

はじめに

これまでの記事で、__construct() や __toString() といった「マジックメソッド」を数多く解説してきましたが、今回取り上げる __LINE__ は少し毛色が異なり、PHPに標準で組み込まれているマジック定数の一つです。

__LINE__ は、それが書かれているソースコード上の現在の行番号に、コンパイル時に置き換えられる特殊な定数です。通常の定数とは異なり、値が固定されているわけではなく、記述された場所によって展開される値が変わります。デバッグログの出力、カスタムエラーハンドラの実装、テストの失敗箇所の特定など、「このコードは何行目で実行されているか」を知りたい場面で活躍します。本記事では基本的な使い方から、他のマジック定数との組み合わせ、実践的な活用例まで詳しく解説します。


定数概要

項目内容
定数名__LINE__
種別マジック定数(コンパイル時に値が決定する特殊な定数)
値の型整数(int)
値の内容その定数が記述されている行の、ファイル内での行番号
対応バージョンPHP 4以降
関連するマジック定数__FILE__, __DIR__, __FUNCTION__, __CLASS__, __METHOD__, __NAMESPACE__
評価タイミング実行時ではなく、スクリプトの解析(コンパイル)時に値が決定する

動作の仕組み(イメージ図)

  ファイル: /app/src/Logger.php

  1: <?php
  2:
  3: function logCurrentLine() {
  4:     echo __LINE__;    ← この行は4行目
  5: }
  6:
  7: logCurrentLine();
              │
              ▼
  出力される値: 4
  (実行されたタイミングに関わらず、
   __LINE__が「書かれている」行番号が使われる)

ポイントは、__LINE__ の値が実行時の呼び出し元ではなく、その定数自体がソースコード上に記述されている行を表すという点です。関数やメソッドの中に書かれた __LINE__ は、その関数が何度呼び出されても、常に同じ行番号(その __LINE__ が書かれた行)を返します。


実践サンプル7選

例1:基本的な使い方

<?php

echo '現在の行番号: ' . __LINE__ . PHP_EOL; // この行の行番号が出力される

function showLine(): void
{
    echo '関数内の行番号: ' . __LINE__ . PHP_EOL;
}

showLine();

例2:デバッグログに呼び出し位置を記録するクラス

<?php

class DebugLogger
{
    /**
     * __LINE__と__FILE__を組み合わせることで、
     * ログがどのファイルの何行目から出力されたかを記録できる
     */
    public static function log(string $message, string $file, int $line): void
    {
        $basename = basename($file);
        echo "[{$basename}:{$line}] {$message}" . PHP_EOL;
    }
}

DebugLogger::log('処理を開始しました', __FILE__, __LINE__);
// 何か処理...
DebugLogger::log('処理が完了しました', __FILE__, __LINE__);

例3:アサーション失敗時に行番号付きでエラーを報告する

<?php

function myAssert(bool $condition, string $message, int $line): void
{
    if (!$condition) {
        throw new AssertionError("{$message}({$line}行目で失敗)");
    }
}

$value = 10;

try {
    myAssert($value === 10, '値は10であるべき', __LINE__);
    echo 'アサーション1: 成功' . PHP_EOL;

    myAssert($value === 20, '値は20であるべき', __LINE__);
    echo 'アサーション2: 成功' . PHP_EOL;
} catch (AssertionError $e) {
    echo 'アサーション失敗: ' . $e->getMessage() . PHP_EOL;
}

例4:複数行にまたがるコードブロックの開始位置を記録する

<?php

class CodeBlockTracker
{
    private array $blocks = [];

    public function markStart(string $blockName, int $line): void
    {
        $this->blocks[$blockName] = ['start' => $line];
    }

    public function markEnd(string $blockName, int $line): void
    {
        $this->blocks[$blockName]['end'] = $line;
    }

    public function getReport(): array
    {
        return $this->blocks;
    }
}

$tracker = new CodeBlockTracker();

$tracker->markStart('データ処理ブロック', __LINE__);
$data = [1, 2, 3];
$sum = array_sum($data);
echo "合計: {$sum}" . PHP_EOL;
$tracker->markEnd('データ処理ブロック', __LINE__);

print_r($tracker->getReport());

例5:カスタム例外に発生位置の詳細情報を追加する

<?php

class DetailedException extends RuntimeException
{
    public function __construct(string $message, private int $customLine, private string $customFile)
    {
        parent::__construct($message);
    }

    public function getDetailedLocation(): string
    {
        return "{$this->customFile}:{$this->customLine}";
    }
}

function riskyOperation(): void
{
    if (true) { // 何らかの失敗条件を想定
        throw new DetailedException('処理に失敗しました', __LINE__, __FILE__);
    }
}

try {
    riskyOperation();
} catch (DetailedException $e) {
    echo $e->getMessage() . ' (' . $e->getDetailedLocation() . ')' . PHP_EOL;
}

例6:ログメッセージに自動的に位置情報を埋め込むトレイト

<?php

trait LocationAwareLogging
{
    /**
     * __LINE__はマクロのように呼び出し側で評価されるため、
     * このトレイト自体の行番号ではなく、
     * 呼び出し元のコードで__LINE__を渡してもらう設計にする
     */
    protected function logWithLocation(string $message, int $line, string $file): void
    {
        printf("[%s:%d] %s\n", basename($file), $line, $message);
    }
}

class OrderService
{
    use LocationAwareLogging;

    public function process(): void
    {
        $this->logWithLocation('注文処理を開始', __LINE__, __FILE__);
        // 何らかの処理...
        $this->logWithLocation('注文処理が完了', __LINE__, __FILE__);
    }
}

(new OrderService())->process();

例7:__LINE__とdebug_backtrace()の役割の違いを確認する

<?php

function innerFunction(): void
{
    // __LINE__はこの行自体の行番号を表す
    echo '現在の行(innerFunction内): ' . __LINE__ . PHP_EOL;

    // debug_backtrace()を使えば、呼び出し元の行番号も取得できる
    $trace = debug_backtrace();
    echo '呼び出し元の行番号: ' . $trace[0]['line'] . PHP_EOL;
}

function outerFunction(): void
{
    innerFunction(); // この行番号がdebug_backtrace()で取得される
}

outerFunction();

関連機能との比較

機能役割__LINE__との違い
__LINE__現在の行番号を取得本記事の対象。記述された行そのものの番号を表す
__FILE__現在のファイルの絶対パスを取得行番号ではなく、ファイルパスの情報を提供する
__FUNCTION__現在の関数名を取得位置情報ではなく、名前の情報を提供する
__CLASS__ / __METHOD__現在のクラス名・メソッド名を取得同上、名前に関する情報を提供する
debug_backtrace()呼び出し履歴(スタックトレース)を取得__LINE__とは異なり、呼び出し元を含む複数の階層の行番号を動的に取得できる

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

  1. 実行時の呼び出し元の行ではなく、定義された行を表す 関数やメソッドの内部に書かれた __LINE__ は、その関数が何回・どこから呼ばれても、常に関数内でその __LINE__ が書かれている行番号を返します。呼び出し元の行番号が欲しい場合は debug_backtrace() を使う必要があります(例7を参照)。
  2. 複数行にまたがる式の中での挙動に注意する 複数行にわたる関数呼び出しの引数リストの中などに __LINE__ を記述した場合、PHPのバージョンや記述位置によって、どの行番号として評価されるかが直感と異なることがあります。できるだけシンプルな文脈で使うのが安全です。
  3. includeやrequireされたファイル内では、そのファイル内の行番号になる include や require で読み込んだファイル内に __LINE__ がある場合、呼び出し元のファイルの行番号ではなく、実際にそのコードが書かれているインクルードされたファイル自身の行番号が返されます。
  4. eval()内での挙動 eval() によって動的に実行されるコード内で __LINE__ を使う場合、その評価されたコード文字列内での行番号になります。元のPHPファイル全体における行番号とは異なる点に注意しましょう。
  5. 本番環境でのデバッグ情報の露出に注意する __LINE__ や __FILE__ を使ってエラーメッセージに詳細な位置情報を含めると、デバッグには便利ですが、本番環境でこうした内部情報がエンドユーザーに表示されると、システム構造の推測材料を与えてしまう可能性があります。ログにのみ出力し、ユーザー向けの表示には含めないといった配慮が望ましいです。

まとめ

観点まとめ
何をする定数かそれが記述されているソースコード上の行番号に、コンパイル時に置き換わる
主な用途デバッグログへの位置情報付与、アサーション失敗時のエラー報告、例外の発生箇所特定
値の決まり方実行時の呼び出し元ではなく、__LINE__自体が書かれている行で決まる
関連するマジック定数__FILE__、__FUNCTION__、__CLASS__など、他にも多数存在する
注意点呼び出し元の行が欲しい場合はdebug_backtrace()を使うこと、本番環境での情報露出への配慮

__LINE__ は、シンプルながらデバッグやエラーハンドリングにおいて非常に実用的なマジック定数です。__FILE__ などの他のマジック定数と組み合わせることで、「どのファイルの何行目で何が起きたか」を正確に記録でき、問題発生時の調査効率を大きく向上させることができます。

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