はじめに
これまでの記事で、__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__とは異なり、呼び出し元を含む複数の階層の行番号を動的に取得できる |
よくある落とし穴(注意点)
- 実行時の呼び出し元の行ではなく、定義された行を表す 関数やメソッドの内部に書かれた
__LINE__は、その関数が何回・どこから呼ばれても、常に関数内でその__LINE__が書かれている行番号を返します。呼び出し元の行番号が欲しい場合はdebug_backtrace()を使う必要があります(例7を参照)。 - 複数行にまたがる式の中での挙動に注意する 複数行にわたる関数呼び出しの引数リストの中などに
__LINE__を記述した場合、PHPのバージョンや記述位置によって、どの行番号として評価されるかが直感と異なることがあります。できるだけシンプルな文脈で使うのが安全です。 - includeやrequireされたファイル内では、そのファイル内の行番号になる
includeやrequireで読み込んだファイル内に__LINE__がある場合、呼び出し元のファイルの行番号ではなく、実際にそのコードが書かれているインクルードされたファイル自身の行番号が返されます。 eval()内での挙動eval()によって動的に実行されるコード内で__LINE__を使う場合、その評価されたコード文字列内での行番号になります。元のPHPファイル全体における行番号とは異なる点に注意しましょう。- 本番環境でのデバッグ情報の露出に注意する
__LINE__や__FILE__を使ってエラーメッセージに詳細な位置情報を含めると、デバッグには便利ですが、本番環境でこうした内部情報がエンドユーザーに表示されると、システム構造の推測材料を与えてしまう可能性があります。ログにのみ出力し、ユーザー向けの表示には含めないといった配慮が望ましいです。
まとめ
| 観点 | まとめ |
|---|---|
| 何をする定数か | それが記述されているソースコード上の行番号に、コンパイル時に置き換わる |
| 主な用途 | デバッグログへの位置情報付与、アサーション失敗時のエラー報告、例外の発生箇所特定 |
| 値の決まり方 | 実行時の呼び出し元ではなく、__LINE__自体が書かれている行で決まる |
| 関連するマジック定数 | __FILE__、__FUNCTION__、__CLASS__など、他にも多数存在する |
| 注意点 | 呼び出し元の行が欲しい場合はdebug_backtrace()を使うこと、本番環境での情報露出への配慮 |
__LINE__ は、シンプルながらデバッグやエラーハンドリングにおいて非常に実用的なマジック定数です。__FILE__ などの他のマジック定数と組み合わせることで、「どのファイルの何行目で何が起きたか」を正確に記録でき、問題発生時の調査効率を大きく向上させることができます。
