Магическая константа __LINE__ в PHP: отладка и трассировка

Раздел: Основы PHP -> Отладка

Магическая константа __LINE__ в PHP возвращает номер текущей строки в файле, где она используется. Эта возможность активно применяется для отладки, логирования и точного указания места возникновения ошибок. Ниже рассмотрены основные способы применения __LINE__ с примерами кода и пояснениями.

Основное эффективное решение

Как указать точное местоположение ошибки в коде?

Наиболее распространённый и действенный способ – включить __LINE__ вместе с __FILE__ в сообщение об ошибке, передаваемое функциям trigger_error() или error_log(). Это позволяет сразу определить, в каком файле и на какой строке произошла проблема.


<?php
$divisor = 0;
if ($divisor == 0) {
    trigger_error('Деление на ноль в строке ' . __LINE__ . ' файла ' . __FILE__, E_USER_WARNING);
}
?>

Line num php (использование __line__ в php)

При выполнении скрипта будет выведено предупреждение с указанием строки и файла. Такой подход ускоряет поиск ошибок в больших проектах.

Типичная проблема:

Если trigger_error() вызывается внутри пользовательской функции, __LINE__ покажет строку внутри этой функции, а не место её вызова. Решение – передавать номер строки из вызывающего кода как аргумент (см. вариант 1).

Различные варианты использования

Как получить номер строки вызывающего кода в функции отладки?

Если для логирования ошибок создаётся отдельная функция, __LINE__ внутри неё всегда будет указывать на строку этой функции. Чтобы получить номер строки, откуда произошёл вызов, нужно передать __LINE__ в качестве параметра.


<?php
function logError($message, $line = null) {
    $line = $line ?? __LINE__; // если не передан, используем строку самой функции
    error_log('Ошибка на строке ' . $line . ': ' . $message);
}

// Использование
logError('Не найден файл', __LINE__);
?>

Php warning session start (предупреждение сессии)

В примере аргумент $line получает значение из вызывающей строки. Если забыть передать второй параметр, номер строки будет определён неверно – это основная ошибка при таком подходе.

Как добавить номер строки в пользовательский обработчик ошибок?

При регистрации собственного обработчика через set_error_handler() параметры $file и $line передаются автоматически. Это наиболее правильный способ для централизованного перехвата всех ошибок.


<?php
set_error_handler(function($severity, $message, $file, $line) {
    echo 'Ошибка [' . $severity . '] в файле ' . $file . ' на строке ' . $line . ': ' . $message;
});

trigger_error('Тестовая ошибка', E_USER_NOTICE);
?>

Php get trace (получение стека вызовов в php)

Результат: Ошибка [1024] в файле /path/to/script.php на строке 6: Тестовая ошибка.

Типичная ошибка:

Игнорирование того, что старый обработчик ошибок нужно сохранять (через возвращаемое значение) или вызывать его вручную, иначе можно потерять стандартное поведение.

Как включить номер строки в сообщение исключения?

При генерации исключения (throw new Exception) можно добавить __LINE__ и __FILE__ в текст сообщения. Это помогает при отладке в try-catch блоках.


<?php
function divide($a, $b) {
    if ($b == 0) {
        throw new Exception('Деление на ноль в файле ' . __FILE__ . ' на строке ' . __LINE__);
    }
    return $a / $b;
}

try {
    echo divide(10, 0);
} catch (Exception $e) {
    echo $e->getMessage();
}
?>

Php get null (ошибка получения get параметров)

Проблема и решение:

Если исключение создаётся внутри метода класса, __LINE__ покажет строку внутри этого метода, а не место вызова throw. Решение – создавать исключение непосредственно в том месте, где возникает ошибка (как в примере выше).

Как отслеживать выполнение SQL-запросов с указанием места их вызова?

В проектах с базой данных полезно логировать каждый запрос вместе с номером строки, откуда он был отправлен. Это упрощает поиск проблемного участка кода.


<?php
function query($sql) {
    // логируем запрос с номером строки вызова
    error_log('SQL: ' . $sql . ' (выполнен из строки ' . __LINE__ . ')');
    // выполнение запроса...
}

query('SELECT * FROM users WHERE id = 1');
?>

Внимание:

Внутри функции query() __LINE__ всегда будет указывать на строку вызова error_log, а не на строку, где вызвана сама query(). Для корректного логирования номер строки вызова следует передавать как аргумент (аналогично варианту 1).

- Index php log (логирование в php)
- Php view source (просмотр исходного кода php)
- Error php stack trace (стек вызовов ошибки php)

Расширенные примеры использования __LINE__

Ниже приведены неочевидные и продвинутые сценарии применения магической константы __LINE__ в PHP.

1. Комбинирование с другими магическими константами

Можно одновременно выводить файл, класс, метод, функцию и строку для полной трассировки.

Пример

<?php
class Logger {
    public static function log($msg) {
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0];
        $info = sprintf(
            '[%s][%s::%s] Line %d: %s',
            basename($trace['file']),
            $trace['class'] ?? 'N/A',
            $trace['function'],
            $trace['line'],
            $msg
        );
        error_log($info);
    }
}

Logger::log('Пользователь авторизован');
?>
[script.php][Logger::log] Line 11: Пользователь авторизован

Здесь debug_backtrace() предоставляет реальный номер строки вызова, а не строку внутри log().

2. Генерация уникальных идентификаторов на основе __LINE__

Комбинируя номер строки с меткой времени, можно создавать практически уникальные строки для логирования или кэширования.

Пример

<?php
function uniqueId() {
    return 'ID_' . __LINE__ . '_' . microtime(true) . '_' . rand(1000, 9999);
}

echo uniqueId();
echo '\n';
echo uniqueId();
?>
ID_4_1681234567.8901_3456
ID_7_1681234567.8902_7890

Каждый вызов даёт разный номер строки (4 и 7), что добавляет вариативность.

3. Использование в рекурсивных функциях

В рекурсивных алгоритмах __LINE__ остаётся неизменным (он вычисляется на этапе компиляции), поэтому для отслеживания глубины требуется передавать номер вызова вручную.

Пример

<?php
function factorial($n, $callLine = null) {
    $callLine = $callLine ?? __LINE__;
    if ($n <= 1) {
        echo "Базовый случай на строке $callLine\n";
        return 1;
    }
    return $n * factorial($n - 1, __LINE__);
}

factorial(3);
?>
Базовый случай на строке 6
Базовый случай на строке 9
Базовый случай на строке 9

Обратите внимание: первый вызов (n=3) передаёт номер строки вызова (9), второй рекурсивный (n=2) тоже передаёт 9, а базовый (n=1) использует строку, переданную из предыдущего вызова. При желании можно сохранять массив строк вызовов.

4. Применение в heredoc/nowdoc для пометки шаблонов

Если в шаблоне используется __LINE__, он будет интерполирован (в heredoc с переменными) или останется строкой (в nowdoc). Это может быть полезно для генерации комментариев.

Пример

<?php
$html = <<<HTML
<!-- Строка шаблона: __LINE__ -->
<h1>Привет</h1>
HTML;
echo $html;
?>
<!-- Строка шаблона: 5 -->
<h1>Привет</h1>

Номер строки (5) соответствует строке heredoc в исходном коде.

5. Условная отладка с __LINE__

Можно проверять, в какой строке выполняется определённая ветка кода, особенно при сложных условиях.

Пример

<?php
$debug = true;
if ($debug) {
    echo "Отладка активна на строке " . __LINE__ . "\n";
}
$value = 42;
if ($value > 10) {
    echo "Значение больше 10 (строка " . __LINE__ . ")\n";
}
?>
Отладка активна на строке 4
Значение больше 10 (строка 8)

Такой подход помогает понять, какие блоки сработали.

6. Логирование с использованием __LINE__ в консольных скриптах

Для CLI-скриптов часто требуется выводить информацию о прогрессе вместе с номером строки.

Пример

<?php
$items = ['a', 'b', 'c'];
foreach ($items as $i => $item) {
    echo "Обработка [$i] '$item' на строке " . __LINE__ . "\n";
    // ... обработка
}
?>
Обработка [0] 'a' на строке 4
Обработка [1] 'b' на строке 4
Обработка [2] 'c' на строке 4

Важно: внутри цикла __LINE__ всегда показывает одну и ту же строку (4), что может быть неинформативно. Для динамической нумерации строк внутри цикла следует использовать переменную-счётчик.

Использование __LINE__ в PHP - comments

En
Line num php (php)