Переменные, типы и строки

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

Переменные

<?php
$name = 'Иван';
$age = 25;
$price = 99.99;
$isStudent = true;

Пиши имена переменных понятно. $x допустимо только если ты математик или очень спешишь.

PHP - динамически типизированный язык: тип значения хранится *в значении*, а не в переменной. Это удобно, но иногда может устроить сюрприз (особенно при [сравнении `==`](./03-conditions.md)).

Базовые типы

<ComparisonTable data={ headers: ["Тип", "Что это", "Пример"], rows: [ ["int", "Целое число", "42"], ["float", "Дробное число (IEEE 754)", "3.14"], ["string", "Строка байтов", ""Hello""], ["bool", "Логический тип", "true / false"], ["array", "Массив (список или словарь)", "[1, 2, 3]"], ["null", "Отсутствие значения", "null"], ["object", "Экземпляр класса", "new User()"] ], } />

Узнать тип значения в рантайме помогает get_debug_type() (PHP 8+) - он точнее старого gettype() и для объектов вернёт полное имя класса:

<?php
echo get_debug_type(42);            // int
echo get_debug_type(3.14);          // float
echo get_debug_type('hello');       // string
echo get_debug_type([1, 2]);        // array
echo get_debug_type(new \DateTime); // DateTime

Строки

Есть два популярных вида кавычек:

<?php
$name = 'Иван';

// Двойные кавычки - интерполяция
echo "Привет, $name!";

// Одинарные - как есть
echo 'Привет, $name!';

Если видишь в выводе $name, а не имя - ты выбрал одинарные кавычки.

Для многострочных шаблонов есть heredoc (с интерполяцией) и nowdoc (без):

<?php
$user = 'Иван';

$letter = <<<TXT
Здравствуйте, $user!
Спасибо за регистрацию.
TXT;

$config = <<<'NOWDOC'
Это $сырая строка - $name не подставится.
NOWDOC;

Конкатенация vs интерполяция vs sprintf

<?php
$first = 'Back';
$second = 'end';

echo $first . $second;                  // через точку
echo "{$first}{$second}";               // интерполяция
echo sprintf('%s%s', $first, $second);  // форматированный вывод

sprintf уместен, когда нужно форматирование (%05d, %.2f) или язык шаблонов отделён от подстановки.

Строка в PHP - это последовательность *байтов*. Длина `strlen('Иван')` вернёт 8 (UTF-8), а не 4. Для работы с символами Unicode - функции из расширения `mbstring`: `mb_strlen`, `mb_substr`, `mb_strtoupper`.

Приведение типов (type casting)

Явное приведение - через (int), (string), (bool), (array):

<?php
$x = '10';
$sum = (int)$x + 5; // 15

$count = 0;
if (!$count) {
    echo 'пусто'; // bool(false) при касте 0 → false
}

PHP охотно приводит типы и сам - это называется type juggling:

<?php
var_dump('10' == 10);   // bool(true) - строка превращается в число
var_dump('0' == false); // bool(true) - оба «пустые»
var_dump(null == 0);    // bool(true) - null приводится к 0

Это поведение - главный источник багов в старом PHP-коде.

strict-сравнение и strict_types

Чтобы не зависеть от юглинга, сравнивай через === (учитывает тип):

<?php
var_dump('10' === 10);         // bool(false) - разные типы
var_dump(0 === false);         // bool(false)
var_dump('hello' === 'hello'); // bool(true)

В начале файла можно объявить строгий режим, и тогда PHP перестанет молча кастовать аргументы функций:

<?php
declare(strict_types=1);

function add(int $a, int $b): int {
    return $a + $b;
}

add(2, 3);     // 5
add('2', '3'); // TypeError - строки не примутся

В современных проектах declare(strict_types=1) ставят в каждом файле как стандарт.

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

Переменная, объявленная вне функции, не видна внутри неё - это спасает от случайных мутаций глобального состояния:

<?php
$name = 'Иван';

function greet() {
    echo "Привет, $name"; // $name == null, warning о неопределённой переменной
}

Чтобы протащить значение - передавай его как аргумент. Ключевое слово global существует, но в нормальном коде его избегают:

<?php
function counter() {
    static $n = 0;     // переменная переживёт вызовы функции
    return ++$n;
}

echo counter(); // 1
echo counter(); // 2
echo counter(); // 3

static внутри функции - это «локальная переменная с памятью между вызовами», часто встречается в кешах и счётчиках.

Инструменты отладки

ФункцияКогда применять
var_dump($x)Подробно: тип + значение, для массивов - рекурсивно
print_r($x)Человекочитаемо, без типов; подойдёт для логов
var_export($x)Возвращает строку, которую можно вставить обратно в код
get_debug_type($x)Тип одной переменной (включая имя класса для объектов)

В Symfony есть удобный helper dump($x) из symfony/var-dumper - он рисует значения с подсветкой и сворачивает большие структуры.

Типичные ошибки

  • Сравнение через ==. "0abc" == 0true, "" == nulltrue. Используй ===, если не уверен.
  • Случайные опечатки в именах. PHP молча создаст новую переменную и присвоит null. Включи error_reporting(E_ALL) в dev - будет warning.
  • Касты вместо валидации. (int)$_GET['id'] спрячет невалидный ввод вместо того, чтобы вернуть 400. Для пользовательского ввода - filter_var или Symfony Validator.
  • Глобальное состояние через global. Делает функции непредсказуемыми и нетестируемыми. Передавай параметры явно.
  • Магические числа и строки. if ($status == 1) - что такое 1? Замени на константу или enum (PHP 8.1+); подробнее про сравнение - в уроке про условия.

Best practices

  • В каждом новом файле - declare(strict_types=1);.
  • Имена переменных на английском, в camelCase: $userEmail, не $user_email и не $e.
  • Для сравнений - === и !== по умолчанию.
  • Для типов в API - mixed, ?T (nullable) и union T|U (PHP 8) точнее, чем безтиповая сигнатура.
  • Не превращай числа в строки преждевременно: формат - это задача вывода, а не модели.

Мини-задание

  • Объяви три переменные разных типов, выведи их через var_dump и get_debug_type.
  • Собери строку приветствия тремя способами: интерполяция, конкатенация, sprintf.
  • Поставь declare(strict_types=1); в файл и попробуй передать в типизированную функцию строку вместо числа - получи и прочитай TypeError.

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