W3docs

__halt_compiler()

В этой статье мы рассмотрим языковую конструкцию PHP __halt_compiler(): её назначение, принцип работы и примеры использования.

В этой статье мы рассмотрим языковую конструкцию PHP __halt_compiler(). Мы дадим обзор конструкции, объясним принцип её работы и приведём примеры использования.

Введение в конструкцию __halt_compiler()

Языковая конструкция __halt_compiler() — уникальная и мощная возможность PHP. Она позволяет встраивать данные непосредственно в PHP-код. Эти данные могут быть чем угодно: текстом, бинарными данными или даже полноценным скриптом.

Когда интерпретатор PHP встречает конструкцию __halt_compiler() в коде, он останавливает парсер и обрабатывает всё, что идёт после вызова, как сырые данные. Это значит, что вы можете встроить данные любого типа в ваш код и обратиться к ним позже как к строке или бинарным данным.

__halt_compiler() — это языковая конструкция, а не обычная функция: она вызывается без аргументов и не возвращает значения. Чаще всего она используется для создания однофайловых инструментов, в которых код и данные поставляются вместе — например, самораспаковывающихся архивов (формат PHAR в PHP использует именно этот приём) или установщиков, несущих собственную полезную нагрузку.

Как использовать конструкцию __halt_compiler()

Конструкция __halt_compiler() очень проста в использовании. Достаточно вызвать её в PHP-коде, а затем разместить данные, которые нужно встроить. Вот пример:

Как использовать конструкцию __halt_compiler()?

php— editable, runs on the server

В этом примере у нас простой PHP-скрипт, который выводит текст с помощью конструкции echo. Затем мы вызываем конструкцию __halt_compiler() и размещаем после неё сырые данные. Когда интерпретатор PHP встречает конструкцию __halt_compiler(), он останавливает парсер и обрабатывает всё, что идёт после неё, как сырые данные.

Доступ к встроенным данным через __COMPILER_HALT_OFFSET__

Когда файл содержит __halt_compiler(), PHP определяет специальную константу __COMPILER_HALT_OFFSET__, которая хранит байтовую позицию первого символа после вызова. Это канонический и самый быстрый способ прочитать встроенную полезную нагрузку из того же файла — вам не нужно самостоятельно искать маркер в файле.

Чтение встроенных данных из того же файла

<?php
// File: bundle.php
echo "Reading the embedded payload...\n";

// Open this very file and jump straight to the data.
$fp = fopen(__FILE__, 'rb');
fseek($fp, __COMPILER_HALT_OFFSET__);
$data = stream_get_contents($fp);
fclose($fp);

echo $data;

__halt_compiler();
Hello from the embedded data!

Здесь __COMPILER_HALT_OFFSET__ указывает на байт сразу после __halt_compiler();, поэтому мы переходим к этой позиции и читаем всё, что следует далее, как сырую полезную нагрузку. Константа существует только в файле, который действительно содержит данную конструкцию.

Доступ к встроенным данным из другого файла

Если вам нужно прочитать полезную нагрузку из другого файла, константа __COMPILER_HALT_OFFSET__ этого файла вам недоступна. В таком случае маркер нужно найти вручную с помощью strpos() и извлечь данные через substr() после загрузки файла посредством file_get_contents().

Вот пример того, как прочитать встроенные данные из отдельного скрипта:

Доступ к встроенным данным в PHP

<?php
// File: embed.php
echo "This is some PHP code.";
__halt_compiler();
This is some raw data.
?>
<?php
// File: extract.php
// Read the script file and extract the data
$content = file_get_contents('embed.php');
$pos = strpos($content, '__halt_compiler();');
if ($pos !== false) {
    $offset = $pos + strlen('__halt_compiler();');
    $data = substr($content, $offset);
    echo $data;
}
?>

В этом примере мы сначала создаём файл со встроенными данными. Затем с помощью отдельного скрипта читаем исходный файл и извлекаем данные, находя маркер __halt_compiler(); и вычисляя смещение. Наконец, мы выводим извлечённые данные с помощью конструкции echo.

Примечание: константа __COMPILER_HALT_OFFSET__ определяется только внутри файла, который непосредственно содержит __halt_compiler(). При чтении другого файла strpos() вернёт false, если маркер отсутствует, поэтому всегда проверяйте позицию с помощью !== false перед вычислением смещения.

Ограничения и особенности

При использовании __halt_compiler() помните о следующих правилах:

  • Вызов должен находиться на верхнем уровне файла. Нельзя размещать конструкцию внутри функции, метода, условного оператора, цикла или любого другого блока. Нарушение этого правила приводит к фатальной ошибке.
  • Только один раз на файл. Файл может содержать __halt_compiler() лишь один раз — она обозначает единственную точку прерывания.
  • Всё, что идёт после, игнорируется как код. Закрывающий тег ?> (если присутствует) и всё, что следует после вызова, обрабатывается как сырые байты и никогда не парсится. Поэтому закрывающий тег ?> обычно опускают, позволяя данным следовать сразу.
  • Это не функция. Нельзя получить её адрес, передать в качестве колбэка или вызвать динамически.

Заключение

Языковая конструкция __halt_compiler() — мощная и гибкая возможность PHP, позволяющая встраивать данные непосредственно в код. Понимая принцип работы конструкции и способы доступа к встроенным данным, вы сможете использовать эту возможность для создания более мощных и гибких PHP-скриптов. Распространённые практические случаи применения включают самораспаковывающиеся архивы, встраивание статических ресурсов (например, CSS или JavaScript) в PHP-логику и создание однофайловых PHP-приложений. Если вам в основном нужно подключать код из других файлов, а не встраивать сырые данные, обратитесь к include.

Практика

Практика
Что такое функция '__halt_compiler' в PHP?
Что такое функция '__halt_compiler' в PHP?
Was this page helpful?