Практический курс PHP для начинающих разработчиков
Основы PHP: от установки до первого проекта
Как установить PHP и выполнить первый скрипт?
Для начала работы с PHP достаточно установить интерпретатор. Самый простой способ - скачать дистрибутив с php.net и добавить его в переменную окружения PATH. После этого можно запускать скрипты из командной строки.
<?php
echo "Привет, мир!";
?>язык программирования php обучение (обучение языку программирования php)
Сохраните код в файл index.php и выполните команду:
php index.php
Привет, мир!
Если вместо вывода отображается текст кода, значит интерпретатор не подключён - проверьте настройки PATH.
Типичная ошибка:
Отсутствие точки с запятой после инструкции вызывает синтаксическую ошибку Parse error: syntax error. Всегда завершайте строки символом ;.
Как запустить PHP без установки полноценного веб-сервера?
PHP включает встроенный сервер для разработки. Достаточно перейти в каталог с проектом и выполнить:
php -S localhost:8000
Теперь сайт доступен по адресу http://localhost:8000. Это удобно для тестирования, но не подходит для продакшена.
Проблема:
Если порт 8000 занят, используйте другой, например php -S localhost:8080. Также встроенный сервер не поддерживает .htaccess, для маршрутизации потребуется подключать файл-роутер.
Как использовать готовые сборки (XAMPP, OpenServer)?
Для начинающих удобны готовые пакеты, включающие PHP, Apache, MySQL и phpMyAdmin. Например, XAMPP - после установки запустите панель управления, активируйте Apache и MySQL, затем поместите файлы в папку htdocs.
<?php
$host = 'localhost';
$user = 'root';
$pass = '';
// Подключение к MySQL
$conn = new mysqli($host, $user, $pass);
if ($conn->connect_error) {
die("Ошибка: " . $conn->connect_error);
}
echo "Подключение успешно";
?>
Подключение успешно
Типичная ошибка:
Пустой пароль у пользователя root в XAMPP - это нормально для локальной разработки, но в продакшене обязательно смените пароль. Если появляется ошибка Warning: mysqli::__construct(): (HY000/2002): No connection could be made, проверьте, запущен ли MySQL.
Как работать с переменными и типами данных?
PHP - язык с динамической типизацией. Переменные начинаются со знака $. Тип определяется автоматически, но можно его привести:
<?php
$name = "Анна"; // строка
$age = 25; // целое
$score = 89.5; // float
$isStudent = true; // boolean
// Приведение типов
$number = (int) "42";
echo gettype($number); // integer
?>
integer
Для проверки типа используйте is_*() функции, например is_string().
Ошибка:
Неявное приведение строки к числу может дать неожиданный результат: "10 яблок" + 5 выдаст 15, но выдаст предупреждение. Используйте приведение явно.
Какие суперглобальные массивы существуют для работы с данными?
PHP предоставляет предопределённые массивы: $_GET, $_POST, $_SERVER, $_SESSION и другие.
<?php
// Форма с методом POST
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = htmlspecialchars($_POST['username'] ?? '');
echo "Привет, $username!";
}
?>
<form method="post">
<input type="text" name="username">
<button>Отправить</button>
</form>
Всегда фильтруйте входные данные с помощью htmlspecialchars() для предотвращения XSS-атак.
Ошибка:
Если форма отправлена методом GET, массив $_POST будет пуст, а данные окажутся в $_GET. Проверяйте метод запроса через $_SERVER['REQUEST_METHOD'].
Как подключиться к базе данных MySQL через PDO?
PDO - современный способ работы с БД, поддерживающий подготовленные выражения для защиты от SQL-инъекций.
<?php
try {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute([':email' => 'user@example.com']);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($user);
} catch (PDOException $e) {
echo "Ошибка: " . $e->getMessage();
}
?>
Array
(
[id] => 1
[email] => user@example.com
[name] => Иван
)
Используйте исключения для обработки ошибок - это упрощает отладку.
Типичная ошибка:
Если в запросе используется неправильное имя таблицы или поля, PDO выбросит исключение с понятным сообщением. Однако не показывайте детали ошибки пользователю в продакшене - логируйте их.
Какие альтернативы PDO существуют для работы с MySQL?
Классический mysqli - процедурный или объектно-ориентированный интерфейс. Пример:
<?php
$mysqli = new mysqli('localhost', 'root', '', 'test');
if ($mysqli->connect_errno) {
die("Ошибка подключения: " . $mysqli->connect_error);
}
$result = $mysqli->query("SELECT * FROM users WHERE email='user@example.com'");
// Опасность: прямая подстановка - возможна SQL-инъекция
// Лучше использовать подготовленные выражения с mysqli::prepare()
$stmt = $mysqli->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param('s', $email);
$email = 'user@example.com';
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
print_r($user);
?>
PDO предпочтительнее из-за переносимости между СУБД и более чистого синтаксиса, но mysqli также допустим.
Ошибка:
В процедурном mysqli все функции принимают объект соединения первым аргументом. Легко перепутать порядок параметров в mysqli_stmt_bind_param() - типовая ошибка новичков.
Как создать класс и использовать объекты в PHP?
ООП в PHP начинается с ключевого слова class. Определим простой класс User:
<?php
class User {
public string $name;
private int $age;
public function __construct(string $name, int $age) {
$this->name = $name;
$this->age = $age;
}
public function getAge(): int {
return $this->age;
}
public function greet(): string {
return "Привет, меня зовут {$this->name}";
}
}
$user = new User('Мария', 30);
echo $user->greet() . ", мне " . $user->getAge() . " лет.";
?>
Привет, меня зовут Мария, мне 30 лет.
Свойства могут иметь модификаторы доступа: public, protected, private.
Ошибка:
Попытка обратиться к приватному свойству извне класса вызовет фатальную ошибку Cannot access private property. Используйте геттеры для доступа.
Какие возможности ООП дополнительно полезны: трейты, интерфейсы, абстрактные классы?
Трейты позволяют повторно использовать методы без наследования. Интерфейсы задают контракт. Абстрактные классы - частичную реализацию.
<?php
interface Logger {
public function log(string $message): void;
}
trait Timestampable {
public function getTimestamp(): string {
return date('Y-m-d H:i:s');
}
}
abstract class BaseController {
abstract protected function handle(): void;
public function run(): void {
$this->handle();
}
}
class AppLogger extends BaseController implements Logger {
use Timestampable;
protected function handle(): void {
echo $this->getTimestamp() . ": Логирование...";
}
public function log(string $message): void {
echo $message;
}
}
$logger = new AppLogger();
$logger->run();
$logger->log("Ошибка");
?>
2025-03-22 12:34:56: Логирование...Ошибка
Такая архитектура упрощает тестирование и расширение.
Проблема:
Если трейт и класс содержат метод с одинаковым именем, приоритет имеет метод класса. Это может привести к неочевидному поведению - используйте insteadof для разрешения конфликтов.
Как обрабатывать исключения и ошибки?
Исключения в PHP обрабатываются с помощью блоков try/catch. Для всех ошибок можно установить глобальный обработчик.
<?php
try {
$result = 10 / 0; // DivisionByZeroError
} catch (DivisionByZeroError $e) {
echo "Ошибка: " . $e->getMessage();
} finally {
echo " (блок finally выполняется всегда)";
}
// Глобальный обработчик
set_exception_handler(function(Throwable $e) {
error_log($e->getMessage());
echo "Произошла ошибка, попробуйте позже.";
});
trigger_error("Предупреждение", E_USER_WARNING);
?>
Ошибка: Division by zero (блок finally выполняется всегда) Произошла ошибка, попробуйте позже.
В продакшене не выводите ошибки пользователю - используйте логирование и дружественные сообщения.
Ошибка:
Если не установлен set_exception_handler(), необработанное исключение вызывает фатальную ошибку с выводом стека. В файле php.ini параметр display_errors должен быть выключен на production.
Расширенные примеры кода на PHP
Ниже приведены более сложные и нестандартные примеры, демонстрирующие возможности PHP в реальных задачах.
Пример 1: Простой маршрутизатор для веб-приложения
<?php
// router.php
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$routes = [
'/' => 'home',
'/about' => 'about',
'/contact' => 'contact',
];
if (isset($routes[$uri])) {
$handler = $routes[$uri];
call_user_func($handler);
} else {
http_response_code(404);
echo "404 Не найдено";
}
function home() {
echo "Домашняя страница";
}
function about() {
echo "О нас";
}
function contact() {
echo "Контакты";
}
?>
Запустите встроенный сервер с этим файлом: php -S localhost:8000 router.php.
При переходе на /about выводится "О нас"
Пример 2: Работа с сессиями и безопасное хранение данных
<?php
session_start();
// Генерация CSRF-токена
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// Проверка отправки формы
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'] ?? '')) {
die('Недействительный CSRF-токен');
}
// Обработка формы
$_SESSION['flash'] = 'Данные сохранены';
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
$flash = $_SESSION['flash'] ?? '';
unset($_SESSION['flash']);
?>
<!DOCTYPE html>
<html>
<body>
<?php if ($flash): ?>
<p class="fw-bold"><?= htmlspecialchars($flash) ?></p>
<?php endif; ?>
<form method="post">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<input type="submit" value="Отправить">
</form>
</body>
</html>
Этот код защищает от CSRF-атак и использует flash-сообщения.
После отправки отображается сообщение "Данные сохранены"
Пример 3: Загрузка файла с проверкой типа и размера
<?php
const UPLOAD_DIR = __DIR__ . '/uploads';
const MAX_FILE_SIZE = 2 * 1024 * 1024; // 2 MB
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/gif'];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
$file = $_FILES['file'];
if ($file['error'] !== UPLOAD_ERR_OK) {
die('Ошибка загрузки: ' . $file['error']);
}
if ($file['size'] > MAX_FILE_SIZE) {
die('Слишком большой файл (макс. 2 МБ)');
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mime, ALLOWED_TYPES)) {
die('Недопустимый тип файла');
}
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$newName = bin2hex(random_bytes(16)) . '.' . $ext;
if (!is_dir(UPLOAD_DIR)) {
mkdir(UPLOAD_DIR, 0755, true);
}
if (move_uploaded_file($file['tmp_name'], UPLOAD_DIR . '/' . $newName)) {
echo 'Файл загружен: ' . htmlspecialchars($newName);
} else {
echo 'Ошибка сохранения файла';
}
}
?>
<form method="post" enctype="multipart/form-data">
<input type="file" name="file">
<button>Загрузить</button>
</form>
Проверка MIME-типа через finfo более надёжна, чем проверка расширения.
При загрузке PNG-файла размером 1 МБ выводится "Файл загружен: a1b2c3d4e5f6g7h8.png"
Пример 4: Создание RESTful API для списка задач
<?php
header('Content-Type: application/json');
$method = $_SERVER['REQUEST_METHOD'];
$input = json_decode(file_get_contents('php://input'), true);
// Имитация базы данных
$tasks = [
['id' => 1, 'title' => 'Изучить PHP', 'done' => false],
['id' => 2, 'title' => 'Написать API', 'done' => false],
];
switch ($method) {
case 'GET':
echo json_encode($tasks);
break;
case 'POST':
$newTask = [
'id' => count($tasks) + 1,
'title' => $input['title'] ?? 'Untitled',
'done' => $input['done'] ?? false,
];
// В реальности добавить в БД
echo json_encode($newTask);
break;
default:
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
break;
}
?>
Тестируйте через curl:
curl -X GET http://localhost:8000/api.php
curl -X POST -H "Content-Type: application/json" -d '{"title":"Новая задача"}' http://localhost:8000/api.php
[{"id":1,"title":"Изучить PHP","done":false},{"id":2,"title":"Написать API","done":false}]
Пример 5: Паттерн Singleton для подключения к базе данных
<?php
class Database {
private static ?PDO $instance = null;
private static string $dsn = 'mysql:host=localhost;dbname=test;charset=utf8';
private static string $user = 'root';
private static string $pass = '';
public static function getInstance(): PDO {
if (self::$instance === null) {
self::$instance = new PDO(self::$dsn, self::$user, self::$pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
}
return self::$instance;
}
// Запрещаем создание экземпляров
private function __construct() {}
private function __clone() {}
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
}
// Использование
$pdo = Database::getInstance();
$result = $pdo->query('SELECT COUNT(*) FROM users');
echo $result->fetchColumn(); // Количество пользователей
?>
Singleton гарантирует единственное соединение с БД на протяжении всего запроса.
Например: 15
Пример 6: Автозагрузка классов через Composer
Создайте файл composer.json:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
Затем выполните composer dump-autoload. Теперь классы из папки src загружаются автоматически. Пример класса:
<?php
// src/Calculator.php
namespace App;
class Calculator {
public function add(int $a, int $b): int {
return $a + $b;
}
}
?>
// index.php
require_once __DIR__ . '/vendor/autoload.php';
use App\Calculator;
$calc = new Calculator();
echo $calc->add(5, 3);
?>
8
Автозагрузка избавляет от необходимости писать require для каждого файла.