PHP — язык, на котором работает треть веба
PHP — самый простой способ сделать веб-страницу динамической: код выполняется на сервере и вставляет данные в HTML. На PHP работают WordPress, Bitrix и миллионы сайтов, а деплой на хостинг занимает минуты. Отличный выбор для первого серверного языка.
1. Что такое PHP
PHP (расшифровка — PHP: Hypertext Preprocessor) — скриптовый язык, который выполняется на сервере. Когда посетитель открывает страницу, сервер обрабатывает PHP-код и отдаёт результат — обычно HTML. Посетитель никогда не видит сам код.
- Динамические страницы — данные из базы вставляются прямо в HTML.
- Формы и заказы — приём данных от пользователей.
- CMS — WordPress, Joomla, Bitrix построены на PHP.
- Низкий порог входа — первый рабочий скрипт пишется за пять минут.
| Плюсы | Минусы |
|---|---|
| легко начать | динамическая типизация (ошибки позже) |
| дешёвый хостинг для PHP | прошлый код бывает неаккуратным |
| огромная база готовых решений | для больших проектов нужны дисциплина и фреймворки |
| отличная интеграция с MySQL | не для высоконагруженных реального времени |
2. Установка XAMPP и первая программа
Для локальной разработки проще всего установить XAMPP — готовый набор из PHP, веб-сервера Apache и базы MySQL. Один установщик — и всё работает.
1. Скачай XAMPP с официального сайта apachefriends.org
2. Запусти установщик и установи в C:\xampp
3. Открой панель XAMPP Control Panel
4. Нажми Start напротив Apache и MySQL
5. В браузере открой http://localhostPHP-файлы хранятся в папке htdocs. Создай там файл index.php:
<?php
/* PHP-код начинается тегом */
echo "Привет, мир!";
?>Открой http://localhost/index.php — увидишь «Привет, мир!». PHP тесно переплетается с HTML:
<!DOCTYPE html>
<html lang="ru">
<body>
<h1>Сейчас <?php echo date("Y") ?> год</h1>
<p><?php echo "Код выполнился на сервере"; ?></p>
</body>
</html>3. Переменные, типы и вывод
Переменные в PHP начинаются со знака $. Язык динамически типизирован: тип определяется значением.
<?php
$name = "Аня"; // строка
$age = 25; // целое число
$height = 1.72; // число с плавающей точкой
$active = true; // булево значение
$balance = null; // пустое значение
echo "Привет, $name!"; // двойные кавычки подставляют переменные
echo 'Привет, ' . $name . '!'; // одинарные — только конкатенация (.)
echo gettype($age); // integer — тип значения
?>Основные типы
| Тип | Описание | Пример |
|---|---|---|
| string | строка текста | "привет" |
| int | целое число | 42 |
| float | дробное число | 3.14 |
| bool | истина/ложь | true, false |
| array | массив значений | [1, 2, 3] |
| null | отсутствие значения | null |
// приведение типов
$num = "25";
$num = (int) $num; // 25 честным числом
// проверка существования и пустоты
if (isset($name) && !empty($name)) {
echo $name;
}
// тернарный оператор
$status = $active ? "активен" : "неактивен";4. Массивы
Массив хранит набор значений по ключу. PHP поддерживает и индексные, и ассоциативные массивы.
<?php
$colors = ["красный", "зелёный", "синий"];
echo $colors[0]; // красный
$user = [
"name" => "Аня",
"age" => 25,
"city" => "Москва",
];
echo $user["name"]; // Аня
// добавление
$colors[] = "жёлтый"; // в конец индексного массива
$user["job"] = "разработчик"; // новая пара «ключ => значение»
// перебор
foreach ($colors as $color) {
echo $color . " ";
}
foreach ($user as $key => $value) {
echo "$key: $value; ";
}// полезные функции
count($colors); // количество элементов
sort($colors); // сортировка по значениям
array_merge($a, $b); // объединение массивов
in_array("синий", $colors); // проверка вхождения
array_key_exists("name", $user); // есть ли ключ
implode(", ", $colors); // массив в строку
explode(",", "a,b,c"); // строка в массив// многомерный массив
$cart = [
["product" => "Кроссовки", "price" => 4990],
["product" => "Шапка", "price" => 990],
];
$total = 0;
foreach ($cart as $item) {
$total += $item["price"];
}
echo "Итого: $total";5. Условия и циклы
Управляющие конструкции в PHP привычны любому, кто видел JavaScript: if-else, switch, for, while и foreach.
<?php
$score = 85;
if ($score >= 90) {
echo "Отлично";
} elseif ($score >= 60) {
echo "Хорошо";
} else {
echo "Подтянись";
}
$day = "saturday";
switch ($day) {
case "saturday":
case "sunday":
echo "Выходной";
break;
default:
echo "Рабочий день";
}// циклы
for ($i = 1; $i <= 5; $i++) {
echo $i . " ";
}
$n = 10;
while ($n > 0) {
$n--;
}
$colors = ["красный", "зелёный", "синий"];
foreach ($colors as $color) {
echo $color . " ";
}
// break и continue
foreach (range(1, 10) as $i) {
if ($i % 2 === 0) continue; // пропустить чётные
if ($i > 7) break; // остановиться
echo $i . " "; // 1 3 5 7
}<ul>
<?php foreach ($products as $p): ?>
<li><?= $p["name"] ?> — <?= $p["price"] ?> руб.</li>
<?php endforeach; ?>
</ul>6. Функции
Функции группируют код для переиспользования и делают программу структурированной.
<?php
function add($a, $b) {
return $a + $b;
}
// параметр по умолчанию
function greet($name, $prefix = "Привет") {
return "$prefix, $name!";
}
echo add(2, 3); // 5
echo greet("Аня"); // Привет, Аня!
echo greet("Боря", "Здравствуйте"); // Здравствуйте, Боря!// типизированные параметры и результат
function multiply(int $a, int $b): int {
return $a * $b;
}
// функции принимают массив «по ссылке» только с &
function appendItem(array &$list, $item) {
$list[] = $item;
}
$items = [1, 2];
appendItem($items, 3); // $items теперь [1, 2, 3]
// стрелочные и анонимные функции
$double = fn($x) => $x * 2;
$nums = array_map($double, [1, 2, 3]); // [2, 4, 6]| Элемент | Описание |
|---|---|
| function f($a) | объявление функции |
| return $value; | возврат результата |
| function f($a = 1) | значение параметра по умолчанию |
| f(...$args) | переменное число аргументов |
| fn($x) => expr | стрелочная функция |
7. Формы: $_POST и $_GET
Данные HTML-форм попадают в суперглобальные массивы: $_GET (параметры из URL) и $_POST (тело запроса). Это ядро любого сайта с обратной связью.
<!-- form.html -->
<form action="register.php" method="post">
<label>Имя: <input type="text" name="name"></label><br>
<label>Email: <input type="email" name="email"></label><br>
<button type="submit">Отправить</button>
</form><?php
// register.php — обработчик формы
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$name = trim($_POST["name"] ?? "");
$email = trim($_POST["email"] ?? "");
$errors = [];
if ($name === "") {
$errors[] = "Имя обязательно";
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = "Некорректный email";
}
if ($errors) {
foreach ($errors as $e) {
echo "<p>$e</p>";
}
} else {
echo "<p>Спасибо, $name! Письмо отправлено на $email.</p>";
}
}// GET-параметры: /search.php?q=носки
$query = trim($_GET["q"] ?? "");
echo "Поиск: " . htmlspecialchars($query);Разница GET и POST
| GET | POST |
|---|---|
| данные в URL | данные в теле запроса |
| видно в адресной строке | скрыто от глаз |
| для поиска, фильтров, ссылок | для форм, заказов, изменения данных |
| можно закладой сохранить | не сохраняется как ссылка |
8. Работа с базой данных: PDO
PDO (PHP Data Objects) — единый интерфейс для работы с разными базами: MySQL, PostgreSQL, SQLite. Обязательное правило — prepared statements, защищающие от SQL-инъекций.
<?php
$dsn = "mysql:host=localhost;dbname=shop;charset=utf8mb4";
$user = "root";
$pass = "";
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
]);
} catch (PDOException $e) {
die("Ошибка подключения к базе."); // логируй $e->getMessage()
}
// добавление пользователя — плейсхолдеры вместо склейки строк
$sql = "INSERT INTO users (name, email) VALUES (:name, :email)";
$stmt = $pdo->prepare($sql);
$stmt->execute([
":name" => $name,
":email" => $email,
]);// чтение данных
$rows = $pdo->query("SELECT * FROM products")->fetchAll();
// замена с параметрами
$sql = "SELECT * FROM products WHERE price > ? AND in_stock = 1";
$stmt = $pdo->prepare($sql);
$stmt->execute([1000]);
$rows = $stmt->fetchAll();
foreach ($rows as $row) {
echo $row["name"] . " — " . $row["price"] . " руб.<br>";
}// подготовленный запрос с условием из формы
$id = $_GET["id"] ?? 0;
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$id]);
$product = $stmt->fetch();9. Сессии и куки
HTTP не помнит предыдущие запросы. Куки — маленькие данные на стороне браузера, а сессии — данные на сервере, связанные с куки-идентификатором.
<?php
// старт сессии — обязательно до любого вывода
session_start();
// сохраняем данные
$_SESSION["user_id"] = 42;
$_SESSION["cart"] = [101, 102, 103];
// читаем в другом запросе
if (isset($_SESSION["user_id"])) {
echo "Вы вошли, id = " . $_SESSION["user_id"];
}
// выход — очистка сессии
session_unset();
session_destroy();// куки: setcookie вызывается до вывода HTML
$expires = time() + 60 * 60 * 24 * 30; // +30 дней
setcookie("theme", "dark", $expires, "/", "", false, true);
// чтение куки
$theme = $_COOKIE["theme"] ?? "light";
// удаление — срок в прошлом
setcookie("theme", "", time() - 3600, "/");Куки против сессий
| Куки | Сессии |
|---|---|
| хранятся у клиента (браузере) | хранятся на сервере |
| видно и можно подделать | клиент видит только id сессии |
| подходит для настроек | подходит для корзины, входа |
| размер ~4 КБ | размер ограничен сервером |
10. Основы ООП
Класс — это шаблон объекта: свойства хранят данные, методы — поведение. Классы помогают объединять логику и переиспользовать код.
<?php
class Product
{
public string $name;
public float $price;
private int $stock = 0;
public function __construct(string $name, float $price)
{
$this->name = $name;
$this->price = $price;
}
public function totalCost(int $qty): float
{
return $this->price * $qty;
}
public function isInStock(): bool
{
return $this->stock > 0;
}
}
$shoes = new Product("Кроссовки", 4990);
echo $shoes->name; // Кроссовки
echo $shoes->totalCost(2); // 9980// наследование и переопределение
class DiscountProduct extends Product
{
private float $discount; // 0..1
public function __construct(string $name, float $price, float $discount)
{
parent::__construct($name, $price);
$this->discount = min(1, max(0, $discount));
}
public function totalCost(int $qty): float
{
return parent::totalCost($qty) * (1 - $this->discount);
}
}
$sale = new DiscountProduct("Шапка", 990, 0.2);
echo $sale->totalCost(1); // 792Модификаторы доступа
| Модификатор | Видимость |
|---|---|
| public | доступно отовсюду |
| protected | класс и потомки |
| private | только внутри класса |
11. Деплой на хостинг
Деплой PHP-сайта прост: скопировал файлы на сервер — и сайт работает. Обычный виртуальный хостинг с панелью (cPanel, ISPmanager) справится с задачей за пятнадцать минут.
1. Купи хостинг с поддержкой PHP и MySQL
2. Создай базу данных и пользователя в панели
3. Загрузи файлы проекта в папку public_html (FTP или админка)
4. Настрой подключение к базе через переменные окружения
5. Переведи PHP на актуальную версию в панели
6. Подключи домен и бесплатный SSL-сертификат<?php
// config.php — подключение к базе через переменные среды
$dbHost = getenv("DB_HOST") ?: "localhost";
$dbName = getenv("DB_NAME") ?: "my_shop";
$dbUser = getenv("DB_USER") ?: "root";
$dbPass = getenv("DB_PASS") ?: "";
// на локальном XAMPP переменных нет — берутся значения по умолчаниюЧек-лист перед публикацией
- Выключи отображение ошибок в браузере пользователю (display_errors = Off).
- Удали тестовые файлы и backup-копии из public_html.
- Убедись, что права доступа к файлам не излишне открытые (644/755).
- Проверь, что PHP-файлы в корне сайта не отдают данные базы при запросе.
- Настрой HTTPS и редирект с http на https.
12. Загрузка файлов на сервер
Работа с файлами в PHP начинается с суперглобального массива $_FILES. Никогда не доверяйте имени файла от клиента: проверяйте тип, размер и перемещайте его в изолированную директорию.
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$file = $_FILES['avatar'] ?? null;
if (!$file || $file['error'] !== UPLOAD_ERR_OK) {
exit('Ошибка загрузки');
}
$allowed = ['image/jpeg', 'image/png'];
if (!in_array(mime_content_type($file['tmp_name']), $allowed)) {
exit('Недопустимый тип файла');
}
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$name = bin2hex(random_bytes(8)) . '.' . $ext;
move_uploaded_file($file['tmp_name'],
__DIR__ . '/uploads/' . $name);
}
13. Composer и автозагрузка PSR-4
Composer — менеджер зависимостей PHP. Он качает библиотеки, обновляет их и генерирует автозагрузку: все классы подключаются автоматически по структуре PSR-4, без ручных require.
// composer.json
{
"require": {
"monolog/monolog": "^3.0"
},
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
composer install
require __DIR__ . '/vendor/autoload.php';PSR-4 означает: пространство имён App\Cart соответствует пути src/Cart.php. Класс App\Models\User лежит в src/Models/User.php.
14. Сессии, CSRF и безопасность паролей
Форма без CSRF-защиты принимает подделанные запросы с других сайтов. Стандартный приём — токен в сессии, который проверяется при каждом POST.
<?php
session_start();
if (empty($_SESSION['csrf'])) {
$_SESSION['csrf'] = bin2hex(random_bytes(32));
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
exit('CSRF-проверка не пройдена');
}
}
$hash = password_hash($_POST['password'], PASSWORD_DEFAULT);
$ok = password_verify($_POST['password'], $hash);- Токен обязательно связывай с сессией, а не клади его статично в HTML.
- Хэш пароля храни в колонке 255 символов — bcrypt сам добавляет соль.
- Для cookie сессии ставь флаг SameSite=Lax.
15. Тестирование с PHPUnit
Автотесты ловят регрессии при любых изменениях. PHPUnit — стандарт для PHP: класс с методами test*, набор assertions и запуск из CLI.
use PHPUnit\Framework\TestCase;
final class CalculatorTest extends TestCase
{
public function testAdd(): void
{
$calc = new Calculator();
$this->assertSame(4, $calc->add(2, 2));
}
}
vendor/bin/phpunit tests| Assert | Что проверяет |
|---|---|
| assertSame | значение и тип |
| assertContains | элемент в массиве |
| expectException | ожидаемое исключение |
| assertCount | число элементов |
16. Современный PHP 8: match, readonly, enums
PHP 8 принёс выразительные конструкции, делающие код короче и безопаснее. match — «ранний выход» из длинных if/elseif: он возвращает значение строго и бросает UnhandledMatchError при отсутствии подходящего варианта. readonly свойства и enum — типизированные доменные значения.
enum OrderStatus: string {
case New = 'new';
case Paid = 'paid';
case Done = 'done';
}
function label(OrderStatus $status): string {
return match ($status) {
OrderStatus::New => 'Новый',
OrderStatus::Paid => 'Оплачен',
OrderStatus::Done => 'Выполнен',
};
}
$user = new class { public readonly string $id; };В PHP 8.2 появились только-read классы (objective: readonly), а в 8.3 — типизированные class-константы. Практическая выгода: меньше ветвлений, меньше «магических» строк, IDE предупреждает о пропущенных кейсах enum — пересчёт логики смены статусов становится явным.
17. Работа с REST API и вебхуками
PHP-приложение редко живёт в вакууме: оплата, рассылки, карты — всё это внешние сервисы, с которыми общаются через HTTP. Современный подход — пакет Guzzle или встроенный клиент, но для быстрого прототипа хватает curl. Парсинг ответа JSON — функция json_decode с флагом ассоциативного массива.
$data = [
'amount' => 100,
'currency' => 'USD',
];
$ch = curl_init('https://api.example.com/pay');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
]);
$resp = json_decode(curl_exec($ch), true);
curl_close($ch);Вебхуки — обратное направление: внешний сервис сам присылает события (платёж проведён, письмо открыто). Обязательно валидируйте входящий вебхук: проверяйте подпись (например, HMAC по секрету) и повторяющиеся ID, чтобы один запрос не выполнился дважды. Обработку делайте «идемпотентной».
- Устанавливайте таймауты (CURLOPT_TIMEOUT) — внешние сервисы не всегда быстрые.
- При повторной отправке вебхука храните обработанный ID в кэше.
- Ошибки пишите в лог с контекстом: endpoint, status, body.
18. Очереди и фоновые задачи
Тяжёлые операции (рассылка, обработка файлов, оплата интеграций) не должны блокировать запрос пользователя. Фоновые задачи в PHP запускаются из CLI: скрипт вызывается cron-ом, а между процессами данные передаются через очередь (Redis, RabbitMQ, таблицу в БД). Пользователь получает ответ мгновенно, работа идёт в фоне.
// в обработчике формы — положить задачу в очередь
$redis->lpush('emails', json_encode(['to' => $email,
'subject' => $subject]));
// в фоновом воркере (cron каждую минуту)
while ($job = $redis->rpop('emails')) {
$item = json_decode($job, true);
send_mail($item['to'], $item['subject']);
}Для надёжности очередь должна переживать падение воркера: используйте структуру, где после извлечения задача подтверждается явно (ACK) — например, через список + отдельная «in-progress» очередь, обрабатываемая по таймауту. Объём и повторы считайте как метрики, чтобы вы не теряли задачи молча.
19. Производительность: кэширование и opcache
PHP исполняется построчно каждый запрос, поэтому главный ускоритель — opcache: он хранит скомпилированные байт-коды скриптов в памяти, а не перечитывает файлы с диска при каждом заходе. Включите его и следите за метрикой opcache_get_status() — reuse ratio должен быть высоким.
; php.ini
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 128
opcache.validate_timestamps = 1
opcache.file_update_protection = 2Второй уровень — кэш данных в Redis (или APCu): тяжёлые запросы к БД, HTML-фрагменты и сессии складываются в память и отдаются мгновенно. Ключи подписывайте версиями (users:v2), чтобы инвалидация была предсказуемой, а TTL задавайте под частоту обновления данных.
- Кэшируйте результат функции, а не сам код: $key = 'stats:' . $id.
- Профилируйте Xdebug/Blackfire перед оптимизацией — не «угадывайте» узкое место.
- Следите за memory_limit: утечка в коде «съедает» память воркера за день.
Что дальше
Ты освоил PHP от установки XAMPP до деплоя: переменные, массивы, условия, функции, формы, базы через PDO, сессии и основы ООП. Дальше рекомендуем углубиться в SQL и MySQL, освежить разметку в HTML и попробовать фреймворк Laravel. Основы сайтов в целом — в курсе Хостинг и деплой.