34 вопросов
PHP работает по модели "shared nothing" - каждый запрос обрабатывается независимо. Жизненный цикл:
С opcache шаги 4-6 выполняются один раз - закешированные opcodes используются повторно. JIT (PHP 8.0+) компилирует горячие opcodes в машинный код.
В 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.
PHP по умолчанию использует слабую (нестрогую) типизацию - автоматически приводит типы при необходимости. Например, строка "5" + 3 дает 8.
Строгий режим включается директивой в начале файла:
declare(strict_types=1);
В строгом режиме PHP не выполняет неявное приведение типов для аргументов функций и возвращаемых значений. Если функция ожидает int, а получает string - будет TypeError.
Важно: директива действует на файл, из которого вызывается функция, а не на файл, где функция объявлена. Арифметические операции и сравнения по-прежнему выполняют приведение типов.
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).
Неинициализированные переменные в PHP имеют значение null. Значения по умолчанию при приведении типов:
int - 0float - 0.0string - "" (пустая строка)bool - falsearray - [] (пустой массив)object - пустой объект stdClassДоступ к неинициализированной переменной генерирует E_WARNING (PHP 8+). Доступ к неинициализированному типизированному свойству класса выбрасывает Error.
В одинарных кавычках ('...') строка интерпретируется "как есть". Единственные 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']}"; // сложные выражения в {}
Одинарные кавычки чуть быстрее (нет парсинга переменных), но разница незначительна. Выбор - вопрос стиля.
== - нестрогое сравнение: 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), но == все равно опасен.
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).
Результат: 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 влияет только на вызовы функций, не на арифметику.
Размер int зависит от платформы:
PHP_INT_MAX)При переполнении 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.
Теоретически строка в PHP ограничена 2 GB (2,147,483,647 байт) - это максимум для 32-битного знакового целого, которое используется для хранения длины. На практике ограничение задается memory_limit в php.ini.
Строка в PHP - это массив байтов + длина. Она не null-terminated (как в C), поэтому может содержать любые байты, включая нулевые (\0).
В PHP нет var и := в привычном смысле (это из Go). В PHP переменные создаются при первом присваивании с префиксом $:
$x = 42; // создание переменной
Константы объявляются двумя способами:
const DB_HOST = 'localhost'; // compile-time, только скаляры
define('DB_PORT', 5432); // runtime, можно в условиях
const - объявление на этапе компиляции (нельзя внутри if). define() - вызов функции в runtime (можно в любом месте). Оба создают глобальные константы, доступные без $.
Константы - неизменяемые значения, определяемые один раз.
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';
}Суперглобальные переменные - предопределенные массивы, доступные в любой области видимости без 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 - всегда валидируйте и экранируйте.
Переменные переменных - использование значения одной переменной как имени другой:
$name = 'hello';
$$name = 'world'; // создает $hello = 'world'
echo $hello; // world
echo $$name; // world
Это считается антипаттерном - усложняет отладку, статический анализ и рефакторинг. Вместо этого используйте ассоциативные массивы:
$vars = ['hello' => 'world'];
echo $vars['hello'];Namespaces решают проблему конфликтов имен классов, функций и констант между библиотеками.
namespace App\Models;
class User { /* ... */ }
// Использование
use App\Models\User;
$user = new User();
// Или полное имя
$user = new \App\Models\User();
Правила:
namespace должно быть первой инструкцией в файле (кроме declare)\ - разделитель уровнейuse - импорт для краткости, не загрузка файла\ (например, \Exception)Обе конструкции подключают файл, но различаются поведением при ошибке:
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 автозагрузка решают задачу подключения классов.
Автозагрузка - механизм автоматического подключения файлов при первом обращении к классу.
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';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).
Сессии - механизм хранения данных пользователя между запросами на стороне сервера.
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.
В PHP три области видимости:
$global = 'hello';
function test() {
// $global здесь НЕ доступна!
echo $global; // Warning: undefined
global $global; // теперь доступна
// или $GLOBALS['global']
static $count = 0;
$count++; // сохраняется между вызовами
}
В отличие от многих языков, PHP не имеет блочной области видимости - переменная, объявленная внутри if/for, доступна во всей функции.
global импортирует глобальную переменную в локальную область видимости функции:
$counter = 0;
function increment() {
global $counter;
$counter++;
}
increment();
echo $counter; // 1
Альтернатива - суперглобальный массив $GLOBALS:
function increment() {
$GLOBALS['counter']++;
}
Использование global считается плохой практикой - создает неявные зависимости, усложняет тестирование. Лучше передавать зависимости через параметры функций или Dependency Injection.
Статическая переменная инициализируется один раз и сохраняет значение между вызовами функции:
function counter(): int {
static $count = 0;
return ++$count;
}
echo counter(); // 1
echo counter(); // 2
echo counter(); // 3
Инициализатор выполняется только при первом вызове. Значение сохраняется до конца запроса (не между запросами). В PHP 8.1+ инициализатор может содержать new выражения.
Ссылка в 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 (изменилось)Copy-on-Write (COW) - оптимизация, при которой копирование данных откладывается до момента первой модификации.
$a = str_repeat('x', 1_000_000); // 1 MB
$b = $a; // НЕ копирует данные! $b указывает на те же данные
// Пока $a и $b только читаются - используется одна копия
$b .= 'y'; // ЗДЕСЬ происходит реальное копирование
COW работает для строк, массивов и других типов на уровне zval. Внутренне PHP использует refcount (счетчик ссылок) для отслеживания количества переменных, указывающих на одни данные.
Это объясняет, почему передача больших массивов в функцию по значению не приводит к замедлению, пока массив не модифицируется внутри.
Да, изменится. Объекты в 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.
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 - включить opcachesession.gc_maxlifetime = 1440 - время жизни сессииПроверка: php -i | grep ini или phpinfo(). Переопределение в runtime: ini_set('memory_limit', '256M').
SPL - набор стандартных интерфейсов, классов и функций, встроенных в PHP.
Основные компоненты:
SPL всегда доступен - не нужно подключать расширение.
Итератор - объект, который позволяет обходить коллекцию элементов через единый интерфейс.
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. Они позволяют обрабатывать данные лениво, без загрузки всего в память.
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-контейнеры) - предотвращает утечки памяти.
Основные уровни ошибок:
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);
});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). Отлично подходит для конечных автоматов, статусов, типов.
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 закрывающий маркер можно располагать с отступом - отступ автоматически удаляется из содержимого.
Обратный слеш \ перед именем функции указывает на глобальное пространство имен:
namespace App\Utils;
strlen('hello'); // PHP ищет App\Utils\strlen(), потом \strlen()
\strlen('hello'); // сразу вызывает глобальную функцию
Производительность: с \ PHP не ищет функцию в текущем namespace, что на ~1% быстрее. Более важно - явность: читатель сразу видит, что это встроенная функция.
Инструменты вроде php-cs-fixer могут автоматически добавлять обратный слеш для встроенных функций (правило native_function_invocation).