Что такое поднятие переменных (hoisting) в PHP?

Ответ

В отличие от JavaScript, в PHP нет классического hoisting'а (поднятия) объявлений переменных. Переменная в PHP не существует до момента её явного объявления в коде.

Ключевое правило: Обращение к переменной до её инициализации вызовет предупреждение или ошибку.

// Пример 1: Ошибка доступа к необъявленной переменной
echo $undefinedVar; // Notice: Undefined variable: undefinedVar
$undefinedVar = 10;

// Пример 2: Переменная существует только после объявления внутри области видимости
function test() {
    // echo $localVar; // Если раскомментировать, будет Notice
    $localVar = "I exist now";
    echo $localVar; // Корректно: "I exist now"
}

Особые случаи, которые иногда путают с поднятием:

  1. Глобальные переменные и ключевое слово global:

    $globalVar = "outside";
    function testGlobal() {
        // echo $globalVar; // Notice! $globalVar здесь не определена
        global $globalVar; // Ссылка на глобальную переменную создается здесь
        echo $globalVar; // Корректно: "outside"
    }
    testGlobal();

    Ключевое слово global не "поднимает" переменную, а создаёт ссылку на уже существующую глобальную переменную в момент выполнения строки с global.

  2. Статические переменные внутри функций:

    function counter() {
        static $count = 0; // Инициализация происходит ТОЛЬКО при первом вызове функции
        $count++;
        echo $count;
    }
    counter(); // 1
    counter(); // 2

    Переменная $count сохраняет своё значение между вызовами функции, но это не hoisting. Область видимости статической переменной ограничена функцией.

  3. Поднятие функций и классов:

    // В PHP функции и классы, объявленные обычным образом, "поднимаются".
    foo(); // Работает, выведет "Hello!"
    function foo() {
        echo "Hello!";
    }
    
    // Однако анонимные функции (замыкания), присвоенные переменным, не поднимаются.
    // bar(); // Fatal error, если раскомментировать
    $bar = function() { echo "Anonymous"; };
    $bar(); // Корректно

Вывод:

  • Для переменных: Hoisting отсутствует. Всегда объявляйте и инициализируйте переменные перед использованием.
  • Для функций/классов: Традиционные объявления (через function и class) поднимаются и могут быть вызваны до их места объявления в файле.
  • Область видимости в PHP в основном определяется функциями и классами, а не блоками {}, как в JavaScript (за исключением анонимных функций/замыканий).

Ответ 18+ 🔞

Давай разберём эту дичь с PHP и его "поднятием", а то народ путается, как будто на дворе 2002-й год и все только про JavaScript слышали.

Смотри, в PHP с переменными — полный пиздец, если пытаться вести себя как в JS. Тут никакого классического hoisting'а нет, и точка. Переменная появляется ровно в тот момент, когда ты её написал, а не где-то в облаках. Попробуешь обратиться раньше — получишь по шапке.

// Пример 1: Попытка выебать систему
echo $undefinedVar; // Тебе вежливо скажут: Notice: Undefined variable: undefinedVar. То есть нихуя нет.
$undefinedVar = 10; // А вот теперь есть, но поздно.

Вот главное правило, которое надо вбить себе в башку: переменная не существует, пока ты её не создал в коде. Всё, приехали.

А теперь про те моменты, где народ начинает ебаться с логикой и думать, что что-то "всплывает":

  1. Глобальные переменные и это ёбаное слово global:

    $globalVar = "снаружи";
    function testGlobal() {
        // echo $globalVar; // Разкомментируешь — получишь Notice! Внутри функции этой хуйни пока нет.
        global $globalVar; // Вот эта строчка — не поднятие, а просто создание ссылки на ту самую внешнюю переменную. Прямо здесь и сейчас.
        echo $globalVar; // Теперь работает: "снаружи"
    }
    testGlobal();

    Никакой магии. global — это просто указатель, который ты выставляешь в нужном месте.

  2. Статические переменные в функциях — хитрая жопа:

    function counter() {
        static $count = 0; // Инициализация срабатывает ТОЛЬКО при первом заходе в функцию. Это не поднятие, а особенность работы `static`.
        $count++;
        echo $count;
    }
    counter(); // 1
    counter(); // 2

    Переменная $count живёт между вызовами, но её область видимости — только внутри функции. Это не hoisting, это просто статика, ёпта.

  3. А вот с функциями и классами — да, тут реальный подъём:

    // Обычные функции — да, их можно вызывать хоть сверху, хоть снизу.
    foo(); // Сработает на ура, выведет "Hello!"
    function foo() {
        echo "Hello!";
    }
    
    // Но если ты функцию в переменную запихал (анонимку), то всё, пиши пропало. Её нет, пока не дошёл до строки с присваиванием.
    // bar(); // Fatal error, полный атас, если попробуешь.
    $bar = function() { echo "Anonymous"; };
    $bar(); // А вот теперь окей.

Итог, чтобы не было удивления пиздец:

  • Переменные: Забудь про hoisting. Сначала объявил, потом использовал. Иначе будешь ловить Notice'ы, как дурак.
  • Функции/Классы: Обычные объявления (function, class) — да, они поднимаются, можно вызывать раньше.
  • Область видимости в PHP в основном крутится вокруг функций и классов, а не вокруг любых скобок {}. Хотя с замыканиями уже можно пошалить.

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