Практический курс PHP для начинающих разработчиков

Раздел: Программирование -> Изучение 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 для каждого файла.

Обучение языку программирования PHP - comments

En
язык программирования php обучение (php)