---
title: 'Основы PHP и глобальные переменные: области видимости и суперглобальные массивы | DevSense'
description: 'Подробное руководство по областям видимости переменных в PHP (локальная, глобальная, статическая) и суперглобальным массивам. Лучшие практики, угрозы безопасности и ограничения PHP 8.1+.'
faq:
    - { question: 'В чем разница между локальной, глобальной и статической областями видимости в PHP?', answer: "Локальные переменные существуют только внутри функции, где они объявлены. Глобальные переменные объявляются вне функций и доступны только с помощью ключевого слова 'global' или массива $GLOBALS. Статические переменные объявляются внутри функции, но сохраняют свое значение между вызовами." }
    - { question: 'Почему нельзя переопределять переменную $GLOBALS целиком в PHP 8.1+?', answer: "Начиная с PHP 8.1, движок Zend Engine запрещает переопределение массива $GLOBALS целиком (например, $GLOBALS = []), чтобы оптимизировать производительность доступа к переменным и гарантировать внутреннюю согласованность. При этом изменение отдельных ключей (например, $GLOBALS['myVar'] = 'value') по-прежнему поддерживается." }
    - { question: 'Как безопасно обрабатывать загрузку файлов через $_FILES в PHP?', answer: "Всегда проверяйте статус загрузки через $_FILES['field']['error'], проверяйте размер и MIME-тип на стороне сервера, верифицируйте файл с помощью is_uploaded_file() и перемещайте в безопасную директорию методом move_uploaded_file()." }
    - { question: 'Почему не рекомендуется напрямую использовать $_GET или $_POST в современном PHP?', answer: 'Прямое обращение к суперглобальным массивам привязывает код к глобальному состоянию, что затрудняет юнит-тестирование и мокирование запросов. Кроме того, это обходит механизмы фильтрации, повышая риски XSS-атак и SQL-инъекций. Рекомендуется использовать специальные обертки запроса.' }
published: '2026-06-07'
---
# Основы PHP: области видимости переменных и суперглобальные массивы

**Difficulty Level:** Junior  
**Target PHP Versions:** PHP 7.0+ (with features marked up to PHP 8.3+)

Представьте себе ситуацию: вы выкатываете минорный рефакторинг кода, и вдруг ваш продакшен падает с фатальной ошибкой: `Fatal error: Cannot reassign $GLOBALS`. Или, возможно, вы написали простую форму обратной связи и обнаружили, что злоумышленник внедрил XSS-нагрузку напрямую через `$_POST`, перехватив сессии администраторов.

В PHP области видимости переменных и суперглобальные массивы составляют фундамент передачи данных. Тем не менее, они остаются частым источником загрязнения областей видимости, утечек памяти и критических уязвимостей для разработчиков, переходящих на этот язык. Понимание того, как переменные перемещаются между областями видимости и как ведут себя суперглобальные массивы PHP — особенно в условиях жестких правил современных версий PHP — необходимо для написания безопасного, тестируемого и надежного кода.

---

## Области видимости переменных: локальная, глобальная и статическая

Область видимости определяет видимость и жизненный цикл переменных в вашем коде. В PHP переменные не проникают во вложенные области видимости автоматически, если это не указано явно.

### Локальная область видимости
* **Тезис**: Переменные, объявленные внутри функции или метода класса, полностью изолированы от внешней среды.
* **Почему это важно**: Локальная область видимости предотвращает конфликты имен и нежелательные изменения переменных. Переменная с именем `$user` внутри одной функции не сможет случайно перезаписать переменную `$user`, определенную в другом месте приложения.
* **Пример**:
  ```php
  // app/Services/CalculationService.php
  
  function calculateTotal(float $price, float $tax): float
  {
      $total = $price + ($price * $tax); // Local variable
      return $total;
  }
  
  // Accessing $total here will throw a Warning (Undefined variable $total)
  ```
* **Последствия**: Как только выполнение функции завершается, её локальные переменные уничтожаются, а память освобождается. Любая попытка обратиться к ним из глобальной области видимости приведет к предупреждению об неопределенной переменной.

### Глобальная область видимости
* **Тезис**: Переменные, объявленные вне функций или классов, относятся к глобальной области видимости. Однако они не становятся автоматически доступными внутри функций.
* **Почему это важно**: Глобальные переменные позволяют обмениваться настройками или подключениями к базе данных между разными файлами, но доступ к ним внутри функций требует явного объявления.
* **Пример**:
  ```php
  // config/database.php
  
  $dbDsn = "mysql:host=127.0.0.1;dbname=app_db";
  
  function connect(): PDO
  {
      // Accessing global scope requires the 'global' keyword
      global $dbDsn; 
      
      return new PDO($dbDsn, "root", "secret");
  }
  ```
* **Последствия**: Использование ключевого слова `global` создает скрытые зависимости. Это крайне затрудняет юнит-тестирование, поскольку тестовое окружение не может легко изолировать или заменить (mock) глобальные значения.

### Статическая область видимости
* **Тезис**: Статическая переменная объявляется внутри функции с помощью ключевого слова `static`. Она существует только внутри локальной области видимости этой функции, но сохраняет свое значение между ее последующими вызовами.
* **Почему это важно**: Статические переменные идеально подходят для кэширования ресурсоемких вычислений, отслеживания глубины рекурсии или генерации простых идентификаторов последовательности без загрязнения глобального пространства имен.
* **Пример**:
  ```php
  // app/Utils/SequenceGenerator.php
  
  function getNextSequenceId(): int
  {
      static $id = 0; // Initialized only on the first call
      $id++;
      return $id;
  }
  
  echo getNextSequenceId(); // 1
  echo getNextSequenceId(); // 2
  ```
  > [!NOTE]
  > **PHP Version Note**: Since PHP 8.3+, static variables can be initialized with dynamic expressions (e.g. calling other functions). Prior to PHP 8.3, they could only be initialized with constant values or literals.
* **Последствия**: Переменная сохраняется в памяти на протяжении всего жизненного цикла текущего процесса PHP. Если ваше приложение работает в условиях долгоживущих процессов (таких как RoadRunner или FrankenPHP), статические переменные будут переносить свое состояние между различными HTTP-запросами, что может привести к утечке памяти или смешиванию данных разных пользователей, если их не сбрасывать.

---

## Суперглобальные массивы PHP

Суперглобальные массивы — это встроенные ассоциативные массивы, которые всегда доступны во всех областях видимости на протяжении всего жизненного цикла скрипта. Для доступа к ним вам не нужно использовать ключевое слово `global`.

### 1. `$GLOBALS` (с ограничениями на переопределение в PHP 8.1+)
* **Тезис**: Ассоциативный массив, содержащий ссылки на все переменные, определенные на данный момент в глобальной области видимости скрипта.
* **Почему это важно**: Он предоставляет программный динамический доступ к глобальной области видимости без необходимости объявлять `global $varName`.
* **Пример**:
  ```php
  // app/Utils/DebugHelper.php
  
  $debugMode = true;
  
  function isDebugEnabled(): bool
  {
      // Directly check the global variable through the superglobal array
      return $GLOBALS['debugMode'] ?? false;
  }
  ```
* **PHP 8.1+ Restriction**:
  До PHP 8.1 массив `$GLOBALS` вел себя как стандартный ассоциативный массив, который можно было переопределить целиком или передать по ссылке. Начиная с PHP 8.1+, переопределение всего массива `$GLOBALS` целиком (например, `$GLOBALS = [];` или `$GLOBALS =& $otherArray;`) запрещено и вызывает фатальную ошибку на этапе компиляции или выполнения. Это ограничение было введено для оптимизации скорости поиска переменных в Zend Engine. Изменение отдельных ключей по-прежнему полностью поддерживается:
  ```php
  // app/Utils/LegacyRunner.php
  
  // This throws a Compile Error in PHP 8.1+:
  // $GLOBALS = ['app_env' => 'production']; 
  
  // This is fully supported and correct:
  $GLOBALS['app_env'] = 'production'; 
  ```
* **Последствия**: Унаследованные (legacy) кодовые базы, которые сбрасывали глобальное состояние при очистке тестов в PHPUnit с помощью `$GLOBALS = []`, должны быть переписаны для сброса переменных по ключам.

### 2. `$_SERVER`
* **Тезис**: Содержит информацию о заголовках, путях, расположении скриптов и переменных окружения веб-сервера.
* **Почему это важно**: Необходим для маршрутизации запросов, проверки HTTP-методов (GET, POST) и проверки протокола (HTTP или HTTPS).
* **Пример**:
  ```php
  // public/index.php
  
  $requestUri = $_SERVER['REQUEST_URI'] ?? '/';
  $requestMethod = $_SERVER['REQUEST_METHOD'] ?? 'GET';
  $isHttps = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
  ```
* **Последствия**: Значения, начинающиеся с префикса `HTTP_` (например, `HTTP_USER_AGENT`, `HTTP_X_FORWARDED_FOR`), передаются напрямую клиентом. Им никогда нельзя доверять слепо, так как они могут быть легко подделаны.

### 3. `$_GET`
* **Тезис**: Ассоциативный массив переменных, переданных скрипту через параметры URL (строку запроса).
* **Почему это важно**: Используется для передачи неконфиденциальных параметров навигации, таких как страницы пагинации, фильтры и поисковые запросы.
* **Пример**:
  ```php
  // app/Controllers/SearchController.php
  
  // URL: /search.php?query=php&page=2
  $query = $_GET['query'] ?? '';
  $page = (int)($_GET['page'] ?? 1); // Cast to int for safety
  ```
* **Последствия**: Параметры запроса сохраняются в истории браузера и логах веб-сервера. Никогда не передавайте конфиденциальные данные (пароли, API-ключи или токены сброса пароля) через `$_GET`.

### 4. `$_POST`
* **Тезис**: Ассоциативный массив переменных, переданных скрипту через HTTP POST (например, HTML-формы или тела запросов с типом `application/x-www-form-urlencoded`).
* **Почему это важно**: Используется для передачи больших объемов данных, метаданных файлов и конфиденциальных данных, которые изменяют состояние на сервере.
* **Пример**:
  ```php
  // app/Controllers/RegistrationController.php
  
  if ($_SERVER['REQUEST_METHOD'] === 'POST') {
      // PHP 8.0+ allows throw expressions with null coalescing:
      $email = $_POST['email'] ?? throw new InvalidArgumentException('Email is required');
      $password = $_POST['password'] ?? '';
  }
  ```
* **Последствия**: Если пользователь отправляет форму, размер которой превышает директиву `post_max_size` в `php.ini`, PHP молча отбрасывает тело запроса. Массив `$_POST` при этом окажется пустым, и никакая ошибка автоматически сгенерирована не будет.

### 5. `$_SESSION`
* **Тезис**: Ассоциативный массив, содержащий переменные сессии, доступные текущему скрипту.
* **Почему это важно**: Позволяет сохранять состояние и идентификатор пользователя (например, статус авторизации или содержимое корзины) между множеством несвязанных HTTP-запросов.
* **Пример**:
  ```php
  // app/Services/AuthenticationService.php
  
  // PHP 7.0+ allows passing runtime options to session_start()
  session_start([
      'cookie_httponly' => true,
      'cookie_secure' => true,
      'cookie_samesite' => 'Lax',
  ]);
  
  $_SESSION['is_logged_in'] = true;
  $_SESSION['username'] = 'john_doe';
  ```
* **Последствия**: Функция `session_start()` должна быть вызвана строго до обращения к `$_SESSION`. Если сессионные куки не настроены с флагом `HttpOnly`, идентификатор сессии может быть украден посредством XSS-атаки, что скомпрометирует аккаунт пользователя.

### 6. `$_COOKIE`
* **Тезис**: Ассоциативный массив переменных, переданных клиентом через куки.
* **Почему это важно**: Позволяет считывать предпочтения пользователя на стороне клиента или долгоживущие токены автозаполнения ("Запомнить меня").
* **Пример**:
  ```php
  // app/Services/ThemeService.php
  
  $userTheme = $_COOKIE['preferred_theme'] ?? 'dark';
  ```
* **Последствия**: Куки полностью хранятся на стороне пользователя. Пользователь может вручную изменить значения кук в своем браузере. Никогда не доверяйте значениям `$_COOKIE` критически важную бизнес-логику (например, `$_COOKIE['is_admin'] = 1`).

### 7. `$_ENV`
* **Тезис**: Ассоциативный массив переменных окружения, переданных текущему процессу PHP.
* **Почему это важно**: Защищает конфиденциальные учетные данные (пароли БД, API-ключи внешних сервисов), исключая их жесткое прописывание в исходном коде.
* **Пример**:
  ```php
  // app/Config/Database.php
  
  $dbPassword = $_ENV['DB_PASSWORD'] ?? 'default_password';
  ```
* **Последствия**: Массив `$_ENV` может оказаться пустым, если директива `variables_order` в `php.ini` не содержит букву `"E"` (например, настроена как `"GPCS"`). В таких случаях используйте функцию `getenv()` или скорректируйте конфигурационный файл.

### 8. `$_FILES`
* **Тезис**: Ассоциативный массив, содержащий метаданные о файлах, загруженных на сервер по протоколу HTTP POST.
* **Почему это важно**: Предоставляет безопасный доступ к свойствам загруженного файла (имя, тип, размер, временный путь, код ошибки) для их последующей валидации и сохранения.
* **Пример**:
  ```php
  // app/Services/UploadService.php
  
  if (isset($_FILES['profile_pic']) && $_FILES['profile_pic']['error'] === UPLOAD_ERR_OK) {
      $tempPath = $_FILES['profile_pic']['tmp_name'];
      
      // Strict security validation
      if (is_uploaded_file($tempPath)) {
          $filename = uniqid('avatar_', true) . '.png';
          move_uploaded_file($tempPath, '/var/www/uploads/' . $filename);
      }
  }
  ```
* **Последствия**: Если файлы не перемещены с помощью функции `move_uploaded_file()` во время работы скрипта, PHP автоматически удаляет их из временной директории по завершении выполнения запроса.

---

## Ограничения, компромиссы и чистая архитектура

Хотя глобальные переменные и суперглобальные массивы встроены в PHP для быстроты разработки, современный подход к архитектуре программного обеспечения не рекомендует использовать их напрямую в бизнес-логике.

* **Отсутствие тестируемости**: Прямое использование `$_GET`, `$_POST` или `$_SERVER` связывает ваш сервис с глобальным состоянием. Это делает юнит-тестирование невозможным, поскольку вы не сможете изолировать контекст выполнения, не переопределяя глобальные переменные перед каждым тестом.
* **Угроза безопасности**: Суперглобальные массивы представляют собой сырые, непроверенные пользовательские данные. Чтение `$_POST['username']` напрямую вместо использования валидированного объекта передачи данных (DTO) увеличивает вероятность SQL-инъекций и XSS.
* **Альтернатива в фреймворках**: Современные PHP-фреймворки (такие как Laravel и Symfony) оборачивают суперглобальные массивы в объекты Request и Response. Вместо чтения `$_GET['id']` вы внедряете зависимость `Request`:
  ```php
  // app/Http/Controllers/UserController.php
  
  // Modern framework approach using Dependency Injection
  public function show(Request $request): Response
  {
      $userId = $request->query('id'); // Mockable and testable
      // ...
  }
  ```

---

## Практические рекомендации

1. **Минимизируйте область видимости**: Старайтесь делать переменные локальными. Передавайте переменные в функции в качестве аргументов вместо доступа к ним через ключевое слово `global`.
2. **Учитывайте жизненный цикл в RoadRunner/FrankenPHP**: Если вы используете статические переменные, обязательно сбрасывайте их состояние, если приложение запущено в долгоживущих процессах. В противном случае возможны утечки памяти или некорректное поведение между запросами.
3. **Используйте обертки запроса**: Если вы пишете код на чистом PHP без фреймворка, напишите простой класс-обертку или используйте функцию `filter_input()` вместо прямого чтения суперглобальных массивов.
4. **Готовность к PHP 8.1+**: Убедитесь, что ваш код (или сторонние библиотеки) не пытается переопределить массив `$GLOBALS` целиком или передать его по ссылке.

---

## Проверка знаний

Тест на понимание областей видимости и суперглобальных массивов в PHP.

### Вопрос 1: Что выведет следующий PHP-код?
```php
// app/Http/Controllers/TestController.php

$name = "Alice";

function greet(): string
{
    return "Hello, " . $name;
}

echo greet();
```
- A) `Hello, Alice`
- B) `Hello, ` вместе с предупреждением Undefined Variable Warning
- C) `Hello, $name`

<details>
<summary>Показать ответ и объяснение</summary>

**Ответ: B**  
Переменные, объявленные в глобальной области видимости, не становятся автоматически доступными внутри функций. Поскольку `$name` не объявлена с ключевым словом `global` и не считывается через `$GLOBALS['name']` внутри `greet()`, PHP выдаст предупреждение о неопределенной переменной и вернет `null` (пустую строку).
</details>

### Вопрос 2: Какое из утверждений верно относительно переопределения `$GLOBALS`?
- A) `$GLOBALS` можно переопределять целиком в любых версиях PHP.
- B) Переопределение `$GLOBALS` целиком (например, `$GLOBALS = [];`) вызывает фатальную ошибку начиная с PHP 8.1+.
- C) Изменение отдельных ключей (например, `$GLOBALS['user'] = 'Bob';`) запрещено и не работает в PHP 8.1+.

<details>
<summary>Показать ответ и объяснение</summary>

**Ответ: B**  
Начиная с PHP 8.1, перезапись всего массива `$GLOBALS` целиком больше невозможна из-за оптимизаций таблицы символов в движке Zend Engine. Однако редактирование отдельных ключей (например, `$GLOBALS['user'] = 'Bob'`) остается полностью поддерживаемым и рабочим.
</details>

### Вопрос 3: Почему не стоит слепо доверять значению `$_FILES['input_name']['type']`?
- A) Оно всегда пустое.
- B) Оно вычисляется сервером PHP и часто бывает некорректным.
- C) Оно отправляется браузером (клиентом) и может быть подделано атакующим.

<details>
<summary>Показать ответ и объяснение</summary>

**Ответ: C**  
MIME-тип в `$_FILES['...']['type']` берется напрямую из заголовков HTTP-запроса клиента. Атакующий может загрузить вредоносный `.php` скрипт, подменив заголовок на `image/png`. Всегда проверяйте реальный MIME-тип на стороне сервера с помощью `finfo_file()` или расширения `fileinfo`.
</details>