Mid📖Теория6 min

Переменные

Области видимости, предопределённые переменные, variable variables, статические переменные

Переменные

Основы переменных

Все переменные в PHP начинаются со знака $. Имена чувствительны к регистру и следуют тем же правилам, что и идентификаторы: начинаются с буквы или подчёркивания, далее буквы, цифры, подчёркивания.

<?php
declare(strict_types=1);

// Naming conventions
$userName = 'Alice';      // camelCase — preferred for variables
$order_total = 100.50;    // snake_case — also used, but be consistent
$_private = 'internal';   // leading underscore — sometimes for "private"

// Variables are case-sensitive
$name = 'Alice';
$Name = 'Bob';   // Different variable!

Присваивание по значению и по ссылке

По умолчанию переменные присваиваются по значению -- создаётся копия. Для присваивания по ссылке используется оператор &:

<?php
declare(strict_types=1);

// Assignment by value (copy)
$a = 'Hello';
$b = $a;
$b = 'World';
echo $a; // 'Hello' — original unchanged

// Assignment by reference
$name = 'Alice';
$ref = &$name;
$ref = 'Bob';
echo $name; // 'Bob' — original changed!

// unset() removes reference, not the value
unset($ref);
echo $name; // 'Bob' — value still exists

Ловушка экзамена: unset() на переменной-ссылке удаляет только саму ссылку, но не исходную переменную и не значение. После unset($ref) переменная $name продолжает существовать.

unset() и множественное удаление

<?php
declare(strict_types=1);

$a = 1;
$b = 2;
$c = 3;

// Remove multiple variables at once
unset($a, $b, $c);

// After unset — variable is undefined
// echo $a; // Warning: Undefined variable $a

Области видимости (Scope)

PHP имеет три области видимости: глобальную, локальную (функция) и статическую.

Глобальная и локальная область

<?php
declare(strict_types=1);

$globalVar = 'I am global';

function showVar(): void
{
    // Cannot access $globalVar here!
    // echo $globalVar; // Warning: Undefined variable

    $localVar = 'I am local';
    echo $localVar; // Works
}

showVar();
echo $globalVar; // Works
// echo $localVar; // Warning: Undefined variable

Запомни: В PHP функции НЕ имеют автоматического доступа к переменным внешней области видимости. Это отличает PHP от JavaScript и Python. Каждая функция имеет собственную изолированную область.

Ключевое слово global и $GLOBALS

<?php
declare(strict_types=1);

$counter = 0;

function increment(): void
{
    global $counter;
    $counter++;
}

// Alternative: $GLOBALS superglobal array
function incrementAlt(): void
{
    $GLOBALS['counter']++;
}

increment();
echo $counter; // 1

incrementAlt();
echo $counter; // 2

Ловушка экзамена: global $var создаёт ссылку на глобальную переменную внутри функции. Если сделать unset($var) внутри функции -- удалится только локальная ссылка, глобальная переменная останется. $GLOBALS -- суперглобальный массив, доступный везде без global.

Замыкания и use

<?php
declare(strict_types=1);

$message = 'Hello';

// Closure captures variable by VALUE (copy at time of creation)
$greet = function (string $name) use ($message): string {
    return "{$message}, {$name}!";
};

$message = 'Goodbye';
echo $greet('Alice'); // 'Hello, Alice!' — captured old value

// Capture by REFERENCE
$count = 0;
$increment = function () use (&$count): void {
    $count++;
};

$increment();
$increment();
echo $count; // 2

// Arrow functions (PHP 7.4+) — auto-capture by VALUE
$multiplier = 3;
$multiply = fn(int $x): int => $x * $multiplier;
echo $multiply(5); // 15

Ловушка экзамена: Обычные замыкания (function() use ($var)) захватывают переменную по значению на момент создания замыкания. Стрелочные функции (fn() =>) также захватывают по значению, но автоматически (без use). Для захвата по ссылке нужен & -- работает только в обычных замыканиях.

Статические переменные

Статическая переменная существует только в локальной области функции, но не теряет значение между вызовами:

<?php
declare(strict_types=1);

function counter(): int
{
    static $count = 0;
    return ++$count;
}

echo counter(); // 1
echo counter(); // 2
echo counter(); // 3
// $count is not accessible here — it's local to the function

Статические инициализаторы (PHP 8.3+)

<?php
declare(strict_types=1);

function getCache(): SplFixedArray
{
    // PHP 8.3+: static initializers can use new and expressions
    static $cache = new SplFixedArray(100);
    return $cache;
}

function getConfig(): array
{
    // PHP 8.3+: expressions in static initializers
    static $config = ['timeout' => 30 * 60, 'retries' => 2 + 1];
    return $config;
}

Запомни: До PHP 8.3 статические переменные можно было инициализировать только скалярными значениями и массивами. С PHP 8.3+ поддерживаются выражения, включая new, вызовы функций и арифметические операции.

Variable Variables (Переменные переменных)

PHP позволяет использовать значение одной переменной как имя другой:

<?php
declare(strict_types=1);

$varName = 'hello';
$$varName = 'world';
echo $hello; // 'world'

// Chained variable variables
$a = 'b';
$b = 'c';
$c = 'value';
echo $$$a; // 'value' ($$$a → $$b → $c → 'value')

// Dynamic variable naming
for ($i = 0; $i < 3; $i++) {
    ${'item_' . $i} = "Value {$i}";
}
echo $item_0; // 'Value 0'
echo $item_1; // 'Value 1'
echo $item_2; // 'Value 2'

Ловушка экзамена: Variable variables не работают с суперглобалами ($_GET, $_POST и т.д.) напрямую. Также $$ внутри строки в двойных кавычках интерпретируется неоднозначно -- используйте ${$var} для ясности. На практике variable variables считаются антипаттерном -- используйте массивы.

Предопределённые переменные (Superglobals)

Суперглобалы -- особые массивы, доступные в любой области видимости без global:

$_GET и $_POST

<?php
declare(strict_types=1);

// URL: /search?q=php&page=2
$query = $_GET['q'] ?? '';        // 'php'
$page  = (int)($_GET['page'] ?? 1); // 2

// POST form data
$email = $_POST['email'] ?? '';
$password = $_POST['password'] ?? '';

// $_REQUEST = $_GET + $_POST + $_COOKIE (order depends on php.ini)
$value = $_REQUEST['key'] ?? 'default';

$_SERVER

<?php
declare(strict_types=1);

// Common $_SERVER keys
$_SERVER['REQUEST_METHOD'];   // GET, POST, PUT, DELETE
$_SERVER['HTTP_HOST'];        // example.com
$_SERVER['REQUEST_URI'];      // /path?query=1
$_SERVER['REMOTE_ADDR'];      // Client IP address
$_SERVER['SERVER_PORT'];      // 80, 443
$_SERVER['DOCUMENT_ROOT'];    // /var/www/html
$_SERVER['SCRIPT_NAME'];      // /index.php
$_SERVER['SCRIPT_FILENAME'];  // /var/www/html/index.php
$_SERVER['SERVER_PROTOCOL'];  // HTTP/1.1
$_SERVER['HTTPS'];            // 'on' if HTTPS
$_SERVER['HTTP_USER_AGENT'];  // Browser user agent string
$_SERVER['HTTP_REFERER'];     // Previous page URL (if sent)
$_SERVER['QUERY_STRING'];     // Raw query string: "q=php&page=2"
<?php
declare(strict_types=1);

// Sessions
session_start();
$_SESSION['user_id'] = 42;
$userId = $_SESSION['user_id'] ?? null;
unset($_SESSION['user_id']);
session_destroy();

// Cookies
setcookie('theme', 'dark', time() + 86400, '/', '', true, true);
$theme = $_COOKIE['theme'] ?? 'light';

$_FILES

<?php
declare(strict_types=1);

// File upload structure
// $_FILES['avatar'] contains:
// 'name'      => 'photo.jpg'         — original filename
// 'type'      => 'image/jpeg'        — MIME type (DO NOT trust!)
// 'size'      => 245678              — file size in bytes
// 'tmp_name'  => '/tmp/phpX1Y2Z3'   — temporary path on server
// 'error'     => UPLOAD_ERR_OK       — error code (0 = success)
// 'full_path' => 'photos/photo.jpg'  — PHP 8.1+: relative path from browser

if ($_FILES['avatar']['error'] === UPLOAD_ERR_OK) {
    move_uploaded_file(
        $_FILES['avatar']['tmp_name'],
        '/uploads/' . basename($_FILES['avatar']['name']),
    );
}

$_ENV и CLI-переменные

<?php
declare(strict_types=1);

// Environment variables
$dbHost = $_ENV['DB_HOST'] ?? 'localhost';
// Alternative: getenv('DB_HOST')

// CLI arguments
// php script.php arg1 arg2
$argc; // 3 (count: script name + 2 args)
$argv; // ['script.php', 'arg1', 'arg2']

// Raw request body (not available in $_POST for JSON)
$json = file_get_contents('php://input');
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);

Проверка существования переменных

isset(), empty(), is_null()

Три функции с разным поведением. Таблица ниже -- одна из самых важных для экзамена:

<?php
declare(strict_types=1);

$a = null;
$b = '';
$c = 0;
$d = '0';
$e = [];
$f = false;

//              isset()   empty()   is_null()
// $a (null)    false     true      true
// $b ('')      true      true      false
// $c (0)       true      true      false
// $d ('0')     true      true      false
// $e ([])      true      true      false
// $f (false)   true      true      false
// $undef       false     true      true  (+ Warning)
<?php
declare(strict_types=1);

// isset() — true if variable exists AND is NOT null
$user = ['name' => 'Alice', 'age' => null];
isset($user['name']);  // true
isset($user['age']);   // false (null!)
isset($user['email']); // false (not set)

// empty() — true for null, '', 0, '0', [], false, 0.0
empty('');    // true
empty(' ');   // false! (space is not empty)
empty('0');   // true! (this catches many developers off guard)

// is_null() — strictly checks for null
is_null(null);  // true
is_null('');    // false
is_null(0);     // false

Ловушка экзамена: empty('0') возвращает true! Строка '0' считается пустой. Это одна из самых частых ошибок. isset() не вызывает Warning для неопределённых переменных, а is_null() -- вызывает. isset() возвращает false для null, даже если переменная существует.

Типизация переменных

В PHP переменные не имеют типа -- значения имеют тип. Переменная может менять тип в любой момент:

<?php
declare(strict_types=1);

$value = 42;
echo gettype($value);        // 'integer'
echo get_debug_type($value); // 'int' (PHP 8.0+, more precise)

$value = 'now a string';
echo get_debug_type($value); // 'string'

Приведение типов (Type Casting)

<?php
declare(strict_types=1);

$str = '42abc';

// Explicit casting
$int   = (int) $str;    // 42
$float = (float) $str;  // 42.0
$bool  = (bool) $str;   // true
$arr   = (array) $str;  // ['42abc']

// intval() with different bases
$hex = intval('0xFF', 16); // 255
$oct = intval('077', 8);   // 63
$bin = intval('1010', 2);  // 10

// settype() — modifies variable in place
$var = '100';
settype($var, 'int');
echo $var; // 100 (now integer)

Запомни: get_debug_type() (PHP 8.0+) предпочтительнее gettype(). Он возвращает точные имена типов (int, string, bool) вместо устаревших (integer, string, boolean). Для объектов возвращает FQCN класса.

compact() и extract()

compact()

Создаёт массив из переменных и их значений:

<?php
declare(strict_types=1);

$name = 'John';
$age = 30;
$city = 'Moscow';

$data = compact('name', 'age', 'city');
// ['name' => 'John', 'age' => 30, 'city' => 'Moscow']

// Useful for passing data to templates/views
// $this->render('profile.html', compact('name', 'age', 'city'));

extract()

Обратная операция -- создаёт переменные из массива:

<?php
declare(strict_types=1);

$data = ['city' => 'Moscow', 'country' => 'Russia', 'code' => 7];

extract($data);
echo $city;    // 'Moscow'
echo $country; // 'Russia'
echo $code;    // 7

// IMPORTANT: extract() with flags for safety
extract($data, EXTR_SKIP);      // Don't overwrite existing variables
extract($data, EXTR_PREFIX_ALL, 'data'); // $data_city, $data_country

Ловушка экзамена: extract() без флагов может перезаписать существующие переменные, что считается небезопасным. Никогда не используйте extract() на непроверенных данных (например, extract($_GET) -- это уязвимость!). В современном PHP предпочитайте явную деструктуризацию массивов.


Вопросы с экзамена ZCE

Проверь себя

5 из 33
🧪

Какие из перечисленных являются суперглобальными переменными в PHP? (выберите три)

Выберите все правильные варианты
🧪

Что предпочтительнее для определения типа значения в PHP 8.0+?

🧪

Какие из следующих источников данных можно считать доверенными?

🧪

Что выведет следующий скрипт? ```php $varName = 'abc'; $$varName = '123'; echo $abc; ```

🧪

Что вернёт `isset($arr['key'])` если `$arr = ['key' => null]`?