PHP: Основы языка

34 вопросов

1 Как работает PHP? Жизненный цикл скрипта от запроса до ответа.

PHP работает по модели "shared nothing" - каждый запрос обрабатывается независимо. Жизненный цикл:

  1. Веб-сервер (Nginx/Apache) получает HTTP-запрос и передает его PHP-FPM через FastCGI
  2. PHP-FPM выбирает свободный воркер-процесс
  3. Воркер инициализирует окружение (загрузка расширений, php.ini)
  4. Лексический анализ (токенизация) исходного кода
  5. Парсинг - построение AST (Abstract Syntax Tree)
  6. Компиляция AST в opcodes (байт-код)
  7. Выполнение opcodes виртуальной машиной Zend Engine
  8. Генерация ответа и отправка клиенту
  9. Освобождение всех ресурсов (память, файлы, соединения)

С opcache шаги 4-6 выполняются один раз - закешированные opcodes используются повторно. JIT (PHP 8.0+) компилирует горячие opcodes в машинный код.

Открыть отдельно →
2 Какие типы данных существуют в PHP?

В PHP 8 есть 10 типов данных:

Скалярные (4):

  • int - целые числа (64-бит на x64)
  • float - числа с плавающей точкой (IEEE 754 double)
  • string - строка байтов
  • bool - true или false

Составные (2):

  • array - упорядоченная хеш-таблица
  • object - экземпляр класса

Специальные (2):

  • null - отсутствие значения
  • resource - внешний ресурс (файл, БД)

Callable и iterable - псевдотипы для type hinting. С PHP 8.1 появился enum, с PHP 8.0 - union types, с PHP 8.1 - intersection types, never.

Открыть отдельно →
3 Чем отличается строгая типизация от слабой? Что такое strict mode?

PHP по умолчанию использует слабую (нестрогую) типизацию - автоматически приводит типы при необходимости. Например, строка "5" + 3 дает 8.

Строгий режим включается директивой в начале файла:

declare(strict_types=1);

В строгом режиме PHP не выполняет неявное приведение типов для аргументов функций и возвращаемых значений. Если функция ожидает int, а получает string - будет TypeError.

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

Открыть отдельно →
4 Что такое type hinting? Где используется?

Type hinting (объявление типов) - указание ожидаемых типов для параметров функций, возвращаемых значений и свойств классов.

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

class User {
    public int $age;
    public ?string $email; // nullable
}

Доступные типы: скалярные (int, float, string, bool), составные (array, object), классы/интерфейсы, callable, iterable, self, static, mixed, void, never, null, true, false.

С PHP 8.0 - union types (int|string), с PHP 8.1 - intersection types (A&B), с PHP 8.2 - DNF types ((A&B)|C).

Открыть отдельно →
5 Какие значения по умолчанию имеют переменные разных типов?

Неинициализированные переменные в PHP имеют значение null. Значения по умолчанию при приведении типов:

  • int - 0
  • float - 0.0
  • string - "" (пустая строка)
  • bool - false
  • array - [] (пустой массив)
  • object - пустой объект stdClass

Доступ к неинициализированной переменной генерирует E_WARNING (PHP 8+). Доступ к неинициализированному типизированному свойству класса выбрасывает Error.

Открыть отдельно →
6 Чем отличаются одинарные кавычки от двойных?

В одинарных кавычках ('...') строка интерпретируется "как есть". Единственные escape-последовательности: \\ и \'.

В двойных кавычках ("...") работает интерполяция переменных и расширенные escape-последовательности (\n, \t, \$, \x41, \u{1F600}):

$name = 'World';
echo "Hello, $name\n";     // Hello, World + перевод строки
echo 'Hello, $name\n';     // Hello, $name\n (буквально)
echo "Item: {$arr['key']}"; // сложные выражения в {}

Одинарные кавычки чуть быстрее (нет парсинга переменных), но разница незначительна. Выбор - вопрос стиля.

Открыть отдельно →
7 Чем отличается == от ===?

== - нестрогое сравнение: PHP приводит типы перед сравнением. === - строгое сравнение: проверяет и значение, и тип.

0 == "foo"    // true в PHP 7, false в PHP 8
0 == ""       // true в PHP 7, false в PHP 8
"" == null    // true
"0" == false  // true
"0" == null   // false
0 === "0"     // false (int !== string)
1 === true    // false (int !== bool)
null === false // false

Всегда используйте === если не нужно намеренное приведение типов. Аналогично, !== вместо !=. В PHP 8 сравнение 0 == "foo" изменилось (теперь false), но == все равно опасен.

Открыть отдельно →
8 Что такое приведение типов? Как PHP выполняет жонглирование типами?

Type juggling - автоматическое преобразование типов PHP при операциях:

"5" + 3        // int(8)
"5.5" + 1      // float(6.5)
true + true    // int(2)
"abc" + 0      // int(0) + Warning в PHP 8

Явное приведение (casting):

$x = (int)"42abc";     // 42
$x = (float)"3.14";   // 3.14
$x = (string)42;      // "42"
$x = (bool)"";        // false
$x = (array)$obj;     // объект в массив
$x = intval("0xFF", 16); // 255

В PHP 8 ужесточены правила: нечисловые строки в арифметике вызывают Warning/TypeError (в strict mode).

Открыть отдельно →
9 Что произойдет при сложении числа 3 и строки '4'?

Результат: int(7). PHP автоматически приведет строку "4" к числу 4 и выполнит сложение.

var_dump(3 + '4');         // int(7)
var_dump(3 + '4.5');       // float(7.5)
var_dump(3 + '4abc');      // int(7) + E_WARNING в PHP 8
var_dump(3 + 'abc');       // int(3) + E_WARNING в PHP 8

В PHP 7 Warning не было. В PHP 8 нечисловые строки в арифметике генерируют E_WARNING. В PHP 9 планируется TypeError.

С declare(strict_types=1) это поведение не меняется - strict mode влияет только на вызовы функций, не на арифметику.

Открыть отдельно →
10 Каков максимальный размер int? Что произойдет при переполнении?

Размер int зависит от платформы:

  • 64-бит: от -9,223,372,036,854,775,808 до 9,223,372,036,854,775,807 (PHP_INT_MAX)
  • 32-бит: от -2,147,483,648 до 2,147,483,647

При переполнении PHP автоматически преобразует int в float:

$x = PHP_INT_MAX;     // 9223372036854775807
$x = $x + 1;          // float(9.2233720368548E+18)
var_dump(is_int($x));  // false
var_dump(is_float($x)); // true

Это происходит тихо, без ошибок. Для работы с большими числами без потери точности используйте расширения bcmath или gmp.

Открыть отдельно →
11 Каков максимальный размер string?

Теоретически строка в PHP ограничена 2 GB (2,147,483,647 байт) - это максимум для 32-битного знакового целого, которое используется для хранения длины. На практике ограничение задается memory_limit в php.ini.

Строка в PHP - это массив байтов + длина. Она не null-terminated (как в C), поэтому может содержать любые байты, включая нулевые (\0).

Открыть отдельно →
12 Чем отличается var от :=? (объявление через var, const, define)

В PHP нет var и := в привычном смысле (это из Go). В PHP переменные создаются при первом присваивании с префиксом $:

$x = 42;  // создание переменной

Константы объявляются двумя способами:

const DB_HOST = 'localhost';         // compile-time, только скаляры
define('DB_PORT', 5432);             // runtime, можно в условиях

const - объявление на этапе компиляции (нельзя внутри if). define() - вызов функции в runtime (можно в любом месте). Оба создают глобальные константы, доступные без $.

Открыть отдельно →
13 Что такое константы? Чем const отличается от define()?

Константы - неизменяемые значения, определяемые один раз.

const VERSION = '1.0';      // compile-time
define('API_KEY', 'abc');   // runtime

Различия:

  • const работает на этапе компиляции, нельзя использовать внутри if, циклов, функций
  • define() - runtime-функция, работает в любом месте
  • const можно использовать в классах, define() - нет
  • const поддерживает только скалярные выражения и массивы
  • define() может принимать результат функции

С PHP 8.3 появились типизированные константы классов:

class Config {
    const string VERSION = '2.0';
}
Открыть отдельно →
14 Что такое суперглобальные переменные?

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

  • $_GET - параметры из URL query string
  • $_POST - данные из тела POST-запроса
  • $_REQUEST - объединение $_GET, $_POST, $_COOKIE
  • $_SERVER - информация о сервере и запросе (HTTP_HOST, REQUEST_URI, REMOTE_ADDR)
  • $_SESSION - данные сессии
  • $_COOKIE - HTTP-куки
  • $_FILES - загруженные файлы
  • $_ENV - переменные окружения
  • $GLOBALS - все глобальные переменные

Никогда не доверяйте данным из $_GET, $_POST, $_COOKIE - всегда валидируйте и экранируйте.

Открыть отдельно →
15 Что такое переменные переменных? ($$var)

Переменные переменных - использование значения одной переменной как имени другой:

$name = 'hello';
$$name = 'world';  // создает $hello = 'world'
echo $hello;       // world
echo $$name;       // world

Это считается антипаттерном - усложняет отладку, статический анализ и рефакторинг. Вместо этого используйте ассоциативные массивы:

$vars = ['hello' => 'world'];
echo $vars['hello'];
Открыть отдельно →
16 Что такое пространства имен (namespaces)?

Namespaces решают проблему конфликтов имен классов, функций и констант между библиотеками.

namespace App\Models;

class User { /* ... */ }

// Использование
use App\Models\User;
$user = new User();

// Или полное имя
$user = new \App\Models\User();

Правила:

  • Объявление namespace должно быть первой инструкцией в файле (кроме declare)
  • Обратный слеш \ - разделитель уровней
  • PSR-4 связывает namespace с файловой структурой
  • use - импорт для краткости, не загрузка файла
  • Глобальный namespace - \ (например, \Exception)
Открыть отдельно →
17 Чем отличается require от include? require_once от include_once?

Обе конструкции подключают файл, но различаются поведением при ошибке:

  • include - при отсутствии файла генерирует E_WARNING, скрипт продолжает работу
  • require - при отсутствии файла генерирует E_ERROR, скрипт останавливается
  • include_once / require_once - подключают файл только один раз, игнорируя повторные вызовы
require 'config.php';           // фатальная ошибка если нет
include 'optional_module.php';  // Warning если нет
require_once 'helpers.php';     // подключит только раз

В современном PHP почти не используются напрямую - Composer и PSR-4 автозагрузка решают задачу подключения классов.

Открыть отдельно →
18 Что такое автозагрузка классов? __autoload() vs spl_autoload_register()

Автозагрузка - механизм автоматического подключения файлов при первом обращении к классу.

spl_autoload_register(function (string $class) {
    $file = str_replace('\\', '/', $class) . '.php';
    if (file_exists($file)) {
        require $file;
    }
});

__autoload() - устаревшая функция (deprecated с PHP 7.2, удалена в PHP 8.0). Могла быть только одна.

spl_autoload_register() - современный подход. Позволяет регистрировать несколько автозагрузчиков в цепочке. Composer генерирует оптимизированный автозагрузчик на основе PSR-4, classmap и files.

// Composer делает это за нас
require 'vendor/autoload.php';
Открыть отдельно →
19 Что такое куки (cookies)? Как работают?

Cookies - небольшие данные (до 4 КБ), которые сервер отправляет браузеру через HTTP-заголовок Set-Cookie. Браузер хранит их и отправляет обратно с каждым запросом.

// Установка
setcookie('user', 'john', [
    'expires'  => time() + 3600,
    'path'     => '/',
    'domain'   => '.example.com',
    'secure'   => true,     // только HTTPS
    'httponly'  => true,     // недоступны для JS
    'samesite' => 'Lax',    // защита от CSRF
]);

// Чтение
$user = $_COOKIE['user'] ?? null;

// Удаление (установить expires в прошлое)
setcookie('user', '', time() - 3600);

Важно: setcookie() нужно вызывать до любого вывода (до echo, HTML).

Открыть отдельно →
20 Что такое сессии? Где хранятся? Как переопределить механизм хранения?

Сессии - механизм хранения данных пользователя между запросами на стороне сервера.

session_start();
$_SESSION['user_id'] = 42;      // сохранить
$id = $_SESSION['user_id'];     // прочитать
unset($_SESSION['user_id']);     // удалить
session_destroy();               // уничтожить сессию

По умолчанию данные хранятся в файлах (/tmp/sess_XXXX). Идентификатор сессии передается через cookie PHPSESSID.

Переопределение хранилища через SessionHandlerInterface:

class RedisSessionHandler implements SessionHandlerInterface {
    public function read($id): string { /* Redis GET */ }
    public function write($id, $data): bool { /* Redis SET */ }
    // ... open, close, destroy, gc
}
session_set_save_handler(new RedisSessionHandler());

Также: session.save_handler = redis в php.ini.

Открыть отдельно →
21 Какие области видимости переменных есть в PHP?

В PHP три области видимости:

  • Глобальная - переменные, объявленные вне функций
  • Локальная - переменные внутри функции (включая параметры)
  • Статическая - локальные переменные, сохраняющие значение между вызовами
$global = 'hello';

function test() {
    // $global здесь НЕ доступна!
    echo $global; // Warning: undefined

    global $global; // теперь доступна
    // или $GLOBALS['global']

    static $count = 0;
    $count++;  // сохраняется между вызовами
}

В отличие от многих языков, PHP не имеет блочной области видимости - переменная, объявленная внутри if/for, доступна во всей функции.

Открыть отдельно →
22 Для чего используется слово global?

global импортирует глобальную переменную в локальную область видимости функции:

$counter = 0;

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

increment();
echo $counter; // 1

Альтернатива - суперглобальный массив $GLOBALS:

function increment() {
    $GLOBALS['counter']++;
}

Использование global считается плохой практикой - создает неявные зависимости, усложняет тестирование. Лучше передавать зависимости через параметры функций или Dependency Injection.

Открыть отдельно →
23 Что такое static переменная внутри функции?

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

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

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

Инициализатор выполняется только при первом вызове. Значение сохраняется до конца запроса (не между запросами). В PHP 8.1+ инициализатор может содержать new выражения.

Открыть отдельно →
24 Что такое ссылки в PHP? Чем передача по ссылке отличается от передачи по значению?

Ссылка в PHP - это псевдоним (alias) для переменной, а не указатель на память (как в C).

$a = 1;
$b = &$a;   // $b - ссылка на $a
$b = 2;
echo $a;    // 2 (изменилось!)
unset($b);  // удаляет только псевдоним
echo $a;    // 2 (значение осталось)

По значению - функция получает копию (с copy-on-write):

function add($x) { $x++; }
$n = 5;
add($n);
echo $n; // 5 (не изменилось)

По ссылке - функция работает с оригиналом:

function add(&$x) { $x++; }
$n = 5;
add($n);
echo $n; // 6 (изменилось)
Открыть отдельно →
25 Что такое поведение copy-on-write?

Copy-on-Write (COW) - оптимизация, при которой копирование данных откладывается до момента первой модификации.

$a = str_repeat('x', 1_000_000); // 1 MB
$b = $a;  // НЕ копирует данные! $b указывает на те же данные
// Пока $a и $b только читаются - используется одна копия

$b .= 'y'; // ЗДЕСЬ происходит реальное копирование

COW работает для строк, массивов и других типов на уровне zval. Внутренне PHP использует refcount (счетчик ссылок) для отслеживания количества переменных, указывающих на одни данные.

Это объясняет, почему передача больших массивов в функцию по значению не приводит к замедлению, пока массив не модифицируется внутри.

Открыть отдельно →
26 Что если передать объект в функцию - изменится ли он снаружи?

Да, изменится. Объекты в PHP передаются по "идентификатору объекта" (object handle) - это не совсем по ссылке, но эффект похож:

class User {
    public string $name;
}

function rename(User $user) {
    $user->name = 'New Name'; // изменяет оригинал!
}

$u = new User();
$u->name = 'Old Name';
rename($u);
echo $u->name; // "New Name"

Однако переприсваивание самой переменной не влияет на оригинал:

function replace(User $user) {
    $user = new User(); // НЕ изменяет оригинал
}
replace($u);
echo $u->name; // все еще "New Name"

Для создания независимой копии объекта используйте clone.

Открыть отдельно →
27 Что такое php.ini? Какие важные настройки нужно знать?

php.ini - основной конфигурационный файл PHP. Загружается при старте PHP-FPM.

Ключевые настройки:

  • memory_limit = 128M - лимит памяти на скрипт
  • max_execution_time = 30 - максимальное время выполнения (секунды)
  • upload_max_filesize = 2M - макс. размер загружаемого файла
  • post_max_size = 8M - макс. размер POST-данных
  • display_errors = Off - не показывать ошибки на проде
  • error_reporting = E_ALL - уровень логирования
  • date.timezone = UTC - временная зона
  • opcache.enable = 1 - включить opcache
  • session.gc_maxlifetime = 1440 - время жизни сессии

Проверка: php -i | grep ini или phpinfo(). Переопределение в runtime: ini_set('memory_limit', '256M').

Открыть отдельно →
28 Что такое SPL (Standard PHP Library)?

SPL - набор стандартных интерфейсов, классов и функций, встроенных в PHP.

Основные компоненты:

  • Структуры данных: SplStack, SplQueue, SplPriorityQueue, SplFixedArray, SplDoublyLinkedList, SplHeap, SplMinHeap, SplMaxHeap
  • Итераторы: ArrayIterator, DirectoryIterator, RecursiveDirectoryIterator, FilterIterator, LimitIterator, RegexIterator
  • Интерфейсы: Countable, Iterator, IteratorAggregate, ArrayAccess, Serializable, SplObserver, SplSubject
  • Исключения: LogicException, RuntimeException, InvalidArgumentException, OutOfRangeException, OverflowException, UnderflowException
  • Функции: spl_autoload_register(), class_implements(), class_parents()

SPL всегда доступен - не нужно подключать расширение.

Открыть отдельно →
29 Что такое итераторы?

Итератор - объект, который позволяет обходить коллекцию элементов через единый интерфейс.

class FileLineIterator implements Iterator {
    private $file;
    private int $line = 0;

    public function current(): string { return fgets($this->file); }
    public function key(): int { return $this->line; }
    public function next(): void { $this->line++; }
    public function rewind(): void { rewind($this->file); $this->line = 0; }
    public function valid(): bool { return !feof($this->file); }
}

Объекты, реализующие Iterator или IteratorAggregate, можно использовать в foreach.

SPL-итераторы: ArrayIterator, DirectoryIterator, RecursiveIteratorIterator, FilterIterator, LimitIterator. Они позволяют обрабатывать данные лениво, без загрузки всего в память.

Открыть отдельно →
30 Что такое Weak References и WeakMap?

WeakReference (PHP 7.4+) - ссылка на объект, которая не препятствует сборке мусора:

$obj = new stdClass();
$weak = WeakReference::create($obj);
echo $weak->get() !== null; // true

unset($obj);
echo $weak->get(); // null (объект удален GC)

WeakMap (PHP 8.0+) - хеш-таблица, где ключами являются объекты, и записи автоматически удаляются при уничтожении объекта-ключа:

$cache = new WeakMap();
$user = new User();
$cache[$user] = ['computed' => 'data'];

unset($user);
// запись автоматически удалена из $cache

WeakMap идеален для кешей, привязанных к объектам (ORM, DI-контейнеры) - предотвращает утечки памяти.

Открыть отдельно →
31 Какие уровни ошибок существуют в PHP?

Основные уровни ошибок:

  • E_ERROR - фатальная ошибка, скрипт останавливается (вызов несуществующей функции)
  • E_WARNING - предупреждение, скрипт продолжается (include несуществующего файла)
  • E_NOTICE - уведомление (доступ к неопределенной переменной)
  • E_DEPRECATED - использование устаревшей функции
  • E_PARSE - синтаксическая ошибка
  • E_STRICT - совет по улучшению кода (удален в PHP 8)

Рекомендация: error_reporting = E_ALL на разработке, логирование всех ошибок на проде.

error_reporting(E_ALL);
set_error_handler(function($errno, $errstr, $errfile, $errline) {
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
Открыть отдельно →
32 Что такое enum в PHP?

Enum (PHP 8.1+) - перечислимый тип, представляющий фиксированный набор значений:

// Unit enum (без значений)
enum Status {
    case Active;
    case Inactive;
    case Banned;
}

// Backed enum (со скалярными значениями)
enum Color: string {
    case Red = 'red';
    case Green = 'green';
    case Blue = 'blue';
}

$c = Color::Red;
echo $c->value;              // "red"
$c = Color::from('green');    // Color::Green
$c = Color::tryFrom('xxx');   // null

Enum может содержать методы, реализовывать интерфейсы, использовать трейты. Не может иметь конструктор или свойства (кроме readonly в backed). Отлично подходит для конечных автоматов, статусов, типов.

Открыть отдельно →
33 Что такое heredoc/nowdoc?

Heredoc - многострочная строка с интерполяцией переменных (аналог двойных кавычек):

$name = 'World';
$html = <<<HTML
<div>
    <p>Hello, $name</p>
</div>
HTML;

Nowdoc - многострочная строка без интерполяции (аналог одинарных кавычек):

$sql = <<<'SQL'
SELECT * FROM users
WHERE name = :name
SQL;

С PHP 7.3 закрывающий маркер можно располагать с отступом - отступ автоматически удаляется из содержимого.

Открыть отдельно →
34 Что такое обратный слеш перед глобальными функциями?

Обратный слеш \ перед именем функции указывает на глобальное пространство имен:

namespace App\Utils;

strlen('hello');   // PHP ищет App\Utils\strlen(), потом \strlen()
\strlen('hello');  // сразу вызывает глобальную функцию

Производительность: с \ PHP не ищет функцию в текущем namespace, что на ~1% быстрее. Более важно - явность: читатель сразу видит, что это встроенная функция.

Инструменты вроде php-cs-fixer могут автоматически добавлять обратный слеш для встроенных функций (правило native_function_invocation).

Открыть отдельно →
🧠Квиз 🏆Лидеры 🎯Собесед. 📖Вопросы 📚База зн.