🐘 PHP

Arrays und Zeichenketten in PHP

Массивы и строки в PHP: полное руководство

В PHP массив — самая универсальная структура данных. Один тип делает всё: список, словарь, стек, очередь. Это как швейцарский нож!

Создание массивов

<?php
// Числовой массив
$fruits = ["яблоко", "банан", "вишня"];
$nums   = array(1, 2, 3, 4, 5);

// Ассоциативный
$student = [
    "name"  => "Алиса",
    "age"   => 12,
    "grade" => "6А",
];

// Доступ
echo $fruits[0] . "\n";        // яблоко
echo $student["name"] . "\n";  // Алиса

// Добавление
$fruits[] = "дыня";            // в конец
$fruits[10] = "ежевика";       // по индексу
$student["city"] = "Москва";  // новый ключ

// Многомерный
$matrix = [[1,2,3],[4,5,6],[7,8,9]];
echo $matrix[1][2] . "\n";  // 6
?>

Основные функции массивов

<?php
$nums = [3, 1, 4, 1, 5, 9, 2, 6];

echo count($nums) . "\n";          // 8
echo max($nums) . "\n";            // 9
echo min($nums) . "\n";            // 1
echo array_sum($nums) . "\n";      // 31
echo implode(", ", $nums) . "\n";  // "3, 1, 4, ..."

// Поиск
echo in_array(5, $nums) ? "есть\n" : "нет\n";
echo array_search(9, $nums) . "\n";  // индекс 5

// Сортировка (изменяет оригинал)
sort($nums);   // по возрастанию
rsort($nums);  // по убыванию
echo implode(" ", $nums) . "\n";

// Уникальные
$u = array_unique([1, 2, 2, 3, 3, 3]);
print_r(array_values($u));

// Обращение
$r = array_reverse([1,2,3,4,5]);
print_r($r);

// Срез
$slice = array_slice([10,20,30,40,50], 1, 3);
print_r($slice);  // [20, 30, 40]

// Слияние
$merged = array_merge([1,2,3], [4,5,6]);
print_r($merged);
?>

array_map

<?php
$nums = [1, 2, 3, 4, 5];

$doubled = array_map(fn($n) => $n * 2, $nums);
print_r($doubled);  // [2, 4, 6, 8, 10]

$squares = array_map(fn($n) => $n ** 2, $nums);
print_r($squares);  // [1, 4, 9, 16, 25]

// Несколько массивов
$a = [1, 2, 3];
$b = [10, 20, 30];
$sums = array_map(fn($x, $y) => $x + $y, $a, $b);
print_r($sums);  // [11, 22, 33]

// Имя функции как строка
$names = ["alice", "bob"];
$upper = array_map('strtoupper', $names);
print_r($upper);  // ["ALICE", "BOB"]

// Сложная трансформация
$students = [
    ["name" => "Алиса", "score" => 85],
    ["name" => "Боря",  "score" => 72],
];
$graded = array_map(function($s) {
    $s["grade"] = $s["score"] >= 80 ? "A" : "B";
    return $s;
}, $students);
print_r($graded);
?>

array_filter

<?php
$nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

$evens = array_filter($nums, fn($n) => $n % 2 == 0);
print_r(array_values($evens));  // [2, 4, 6, 8, 10]

// Без функции — убирает ложные значения
$mixed = [1, 0, "hello", "", null, false, 42];
$truthy = array_filter($mixed);
print_r(array_values($truthy));  // [1, "hello", 42]

// Фильтрация сложных структур
$products = [
    ["name" => "Ноутбук", "price" => 50000, "in_stock" => true],
    ["name" => "Мышь",    "price" => 1500,  "in_stock" => false],
    ["name" => "Клава",   "price" => 3000,  "in_stock" => true],
];
$available = array_filter($products,
    fn($p) => $p["in_stock"] && $p["price"] < 10000);
foreach ($available as $p) {
    echo "{$p['name']}: {$p['price']} руб.\n";
}
?>

array_reduce и usort

<?php
$nums = [1, 2, 3, 4, 5];

$sum  = array_reduce($nums, fn($c, $x) => $c + $x, 0);
$prod = array_reduce($nums, fn($c, $x) => $c * $x, 1);
$max  = array_reduce($nums, fn($c, $x) => max($c, $x), PHP_INT_MIN);
echo "Сумма: $sum, Произведение: $prod, Макс: $max\n";

// usort — сортировка с функцией сравнения
$students = [
    ["name" => "Вера",  "gpa" => 4.2],
    ["name" => "Алиса", "gpa" => 4.8],
    ["name" => "Боря",  "gpa" => 3.9],
];
usort($students, fn($a, $b) => $b["gpa"] <=> $a["gpa"]);  // убывание
foreach ($students as $i => $s) {
    echo ($i+1) . ". {$s['name']}: {$s['gpa']}\n";
}
?>

Строковые функции

<?php
$s = "  Hello, World! PHP is great!  ";

// Длина и позиция
echo strlen($s) . "\n";              // 33
echo strpos($s, "PHP") . "\n";       // 17
echo strrpos($s, "!") . "\n";        // последнее !

// Обрезка
echo trim($s) . "\n";                // без пробелов
echo ltrim($s) . "\n";               // слева
echo rtrim($s) . "\n";               // справа

// Замена
echo str_replace("PHP", "Rust", $s) . "\n";
echo str_ireplace("WORLD", "Rust", $s) . "\n"; // без учёта регистра

// Подстрока
echo substr($s, 9, 5) . "\n";        // World
echo substr($s, -7) . "\n";          // great!

// Разбивка
$fruits = "яблоко,банан,вишня";
$arr = explode(",", $fruits);
print_r($arr);

// Объединение
echo implode(" | ", $arr) . "\n";    // яблоко | банан | вишня

// Регистр
echo strtoupper("hello") . "\n";     // HELLO
echo strtolower("WORLD") . "\n";     // world
echo ucfirst("hello world") . "\n";  // Hello world
echo ucwords("hello world") . "\n";  // Hello World
?>

Продвинутые строковые функции

<?php
// PHP 8 — новые функции
echo str_contains("Hello PHP", "PHP") ? "да\n" : "нет\n";
echo str_starts_with("Hello", "He") ? "да\n" : "нет\n";
echo str_ends_with("Hello", "lo") ? "да\n" : "нет\n";

// Форматирование
$price = 1234567.89;
echo number_format($price, 2, '.', ',') . "\n";  // 1,234,567.89
echo sprintf("Цена: %.2f", $price) . "\n";

echo str_repeat("*", 20) . "\n";
echo str_pad("42", 8, "0", STR_PAD_LEFT) . "\n";  // 00000042
echo strrev("Hello") . "\n";                        // olleH

// Регулярные выражения
$email = "user@example.com";
if (preg_match('/^[\w.]+@[\w.]+\.[a-z]{2,}$/i', $email)) {
    echo "Email валидный\n";
}

$html = "<p>Hello <b>World</b></p>";
echo strip_tags($html) . "\n";    // Hello World
echo htmlspecialchars("<script>") . "\n";  // &lt;script&gt;

// Поиск и замена через регулярки
$text = "Телефон: +7(495)123-45-67";
preg_match('/\+?[\d()+\-]+/', $text, $matches);
echo $matches[0] . "\n";
?>

Практика: обработка CSV

<?php
$csv = "Алиса,12,6А,4.8
Боря,13,7Б,3.9
Вера,12,6А,4.5
Гриша,14,8В,4.1";

$lines = explode("\n", trim($csv));
$students = array_map(function($line) {
    [$name, $age, $class, $gpa] = explode(",", $line);
    return ["name"=>$name, "age"=>(int)$age, "class"=>$class, "gpa"=>(float)$gpa];
}, $lines);

// Класс 6А
$cls6a = array_filter($students, fn($s) => $s["class"] === "6А");
usort($cls6a, fn($a, $b) => $b["gpa"] <=> $a["gpa"]);

printf("%-10s %-6s %s\n", "Имя", "Класс", "GPA");
echo str_repeat("-", 25) . "\n";
foreach ($cls6a as $s) {
    printf("%-10s %-6s %.1f\n", $s["name"], $s["class"], $s["gpa"]);
}

$gpas = array_column($cls6a, "gpa");
printf("\nСредний 6А: %.2f\n", array_sum($gpas) / count($gpas));
?>

Типичные ошибки

❌ strpos возвращает 0 (не false!) для позиции 0

$pos = strpos("Hello", "H");  // 0
if ($pos == false) { /* ОШИБКА: 0 == false */ }
if ($pos === false) { /* ✅ строгое */ }

❌ Забыл & и unset после foreach

foreach ($arr as &$item) { $item *= 2; }
// Обязательно:
unset($item);  // иначе $item — ссылка на последний элемент!

Итого

Массив PHP универсален: числовой и ассоциативный — один тип. array_map — преобразование. array_filter — фильтрация. array_reduce — свёртка. usort — сортировка. implode/explode — строка↔массив. Богатая библиотека строк: strlen, strpos, substr, str_replace, trim и другие.

Дополнительные примеры и задачи

Пример 1: Работа с датами и временем

<?php
// PHP имеет богатые встроенные функции для работы с датами
$now = new DateTime();
echo $now->format('d.m.Y H:i:s') . "\n";

// Создание конкретной даты
$birthday = new DateTime('2012-03-15');
$today = new DateTime();
$diff = $today->diff($birthday);
echo "Прошло {$diff->y} лет {$diff->m} месяцев {$diff->d} дней\n";

// Добавление/вычитание времени
$next_week = new DateTime();
$next_week->modify('+7 days');
echo "Через неделю: " . $next_week->format('d.m.Y') . "\n";

$past = new DateTime();
$past->modify('-30 days');
echo "Месяц назад: " . $past->format('d.m.Y') . "\n";

// Форматы даты
$date = new DateTime('2024-01-15 14:30:00');
echo $date->format('d MMMM Y') . "\n";  // нет: это ICU формат
echo $date->format('j F Y') . "\n";      // 15 January 2024
echo $date->format('U') . "\n";          // Unix timestamp

// strtotime — удобное создание дат
$ts = strtotime('next Monday');
echo date('d.m.Y', $ts) . "\n";
$ts2 = strtotime('+2 months');
echo date('d.m.Y', $ts2) . "\n";
?>

Пример 2: Работа с файлами

<?php
// Запись в файл
$data = "Привет, PHP!\nВторая строка\n";
file_put_contents('/tmp/test.txt', $data);
echo "Записано: " . strlen($data) . " байт\n";

// Добавление к файлу
file_put_contents('/tmp/test.txt', "Третья строка\n", FILE_APPEND);

// Чтение файла
$content = file_get_contents('/tmp/test.txt');
echo $content;

// Чтение построчно
$lines = file('/tmp/test.txt', FILE_IGNORE_NEW_LINES);
foreach ($lines as $i => $line) {
    echo "$i: $line\n";
}

// Проверка существования
if (file_exists('/tmp/test.txt')) {
    echo "Файл существует\n";
    echo "Размер: " . filesize('/tmp/test.txt') . " байт\n";
}

// Удаление
unlink('/tmp/test.txt');
echo "Файл удалён\n";
?>

Пример 3: Регулярные выражения

<?php
// Валидация email
function validateEmail(string $email): bool {
    return (bool) preg_match('/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/', $email);
}

$emails = ["user@example.com", "invalid-email", "test@test.ru", "no_at_sign"];
foreach ($emails as $email) {
    echo "$email: " . (validateEmail($email) ? "✅" : "❌") . "\n";
}

// Извлечение данных
$text = "Телефоны: +7(495)123-45-67 и 8-800-555-35-35";
preg_match_all('/[+7|8][\d\s\-()]+/', $text, $matches);
print_r($matches[0]);

// Замена
$html = "<p>Hello <b>World</b></p>";
$clean = preg_replace('/<[^>]+>/', '', $html);
echo $clean . "\n";  // Hello World

// Разбивка по паттерну
$str = "яблоко,  банан;вишня  дыня";
$fruits = preg_split('/[,;\s]+/', trim($str));
print_r(array_filter($fruits));
?>

Практическое задание: калькулятор оценок

<?php
class GradeCalculator {
    private array $subjects = [];

    public function addGrade(string $subject, float $grade): void {
        if ($grade < 1 || $grade > 5) {
            throw new InvalidArgumentException("Оценка должна быть от 1 до 5");
        }
        $this->subjects[$subject][] = $grade;
    }

    public function getAverage(string $subject): float {
        if (!isset($this->subjects[$subject])) {
            throw new RuntimeException("Предмет не найден: $subject");
        }
        return array_sum($this->subjects[$subject]) / count($this->subjects[$subject]);
    }

    public function getOverallAverage(): float {
        $all = [];
        foreach ($this->subjects as $grades) {
            $all = array_merge($all, $grades);
        }
        return empty($all) ? 0.0 : array_sum($all) / count($all);
    }

    public function getReport(): string {
        $lines = ["=== Отчёт об успеваемости ==="];
        foreach ($this->subjects as $subject => $grades) {
            $avg = $this->getAverage($subject);
            $lines[] = sprintf("%-15s: %.2f (оценок: %d)", $subject, $avg, count($grades));
        }
        $lines[] = str_repeat("-", 35);
        $lines[] = sprintf("%-15s: %.2f", "ИТОГО", $this->getOverallAverage());
        return implode("\n", $lines);
    }
}

$calc = new GradeCalculator();
$calc->addGrade("Математика", 5);
$calc->addGrade("Математика", 4);
$calc->addGrade("Математика", 5);
$calc->addGrade("Русский язык", 4);
$calc->addGrade("Русский язык", 5);
$calc->addGrade("Английский", 3);
$calc->addGrade("Английский", 4);

echo $calc->getReport() . "\n";
?>

Полезные PHP-паттерны

Паттерн Repository (хранилище данных)

<?php
interface UserRepositoryInterface {
    public function findById(int $id): ?array;
    public function findAll(): array;
    public function save(array $user): int;
    public function delete(int $id): bool;
}

class InMemoryUserRepository implements UserRepositoryInterface {
    private array $users = [];
    private int $nextId = 1;

    public function findById(int $id): ?array {
        return $this->users[$id] ?? null;
    }

    public function findAll(): array {
        return array_values($this->users);
    }

    public function save(array $user): int {
        $id = $user['id'] ?? $this->nextId++;
        $this->users[$id] = array_merge($user, ['id' => $id]);
        return $id;
    }

    public function delete(int $id): bool {
        if (!isset($this->users[$id])) return false;
        unset($this->users[$id]);
        return true;
    }
}

$repo = new InMemoryUserRepository();
$id1 = $repo->save(['name' => 'Алиса', 'email' => 'alice@test.com']);
$id2 = $repo->save(['name' => 'Боря',  'email' => 'bob@test.com']);

print_r($repo->findAll());
print_r($repo->findById($id1));

$repo->delete($id2);
echo "Осталось: " . count($repo->findAll()) . "\n";
?>

Ещё практики: функции для задач из реальной жизни

Работа с JSON

<?php
// json_encode — PHP → JSON
$data = [
    "name"    => "Алиса",
    "age"     => 12,
    "hobbies" => ["программирование", "чтение"],
    "address" => ["city" => "Москва", "zip" => "123456"],
];

$json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
echo $json . "\n";

// json_decode — JSON → PHP
$json_str = '{"name":"Боря","score":95,"active":true}';
$obj = json_decode($json_str);           // объект
$arr = json_decode($json_str, true);     // массив (флаг true)

echo $obj->name . ": " . $obj->score . "\n";
echo $arr["name"] . ": " . $arr["score"] . "\n";

// Проверка ошибок
$bad = json_decode("{invalid json}", true);
if (json_last_error() !== JSON_ERROR_NONE) {
    echo "Ошибка JSON: " . json_last_error_msg() . "\n";
}

// Сохранение/чтение конфига
$config = ["debug" => true, "db_host" => "localhost", "max_users" => 100];
file_put_contents('/tmp/config.json', json_encode($config));
$loaded = json_decode(file_get_contents('/tmp/config.json'), true);
echo "Debug: " . ($loaded['debug'] ? 'on' : 'off') . "\n";
?>

Работа с сессиями и куками

<?php
// Сессии — данные хранятся на сервере
// session_start() должен быть ПЕРЕД любым выводом

// session_start();
// $_SESSION['user_id']   = 42;
// $_SESSION['user_name'] = "Алиса";
// $_SESSION['logged_in'] = true;

// // На другой странице:
// session_start();
// if ($_SESSION['logged_in'] ?? false) {
//     echo "Добро пожаловать, " . $_SESSION['user_name'] . "!";
// } else {
//     header('Location: /login.php');
// }

// Куки — данные хранятся в браузере
// setcookie('theme', 'dark', time() + 86400 * 30); // на 30 дней
// $theme = $_COOKIE['theme'] ?? 'light';
// echo "Тема: $theme";

// Симуляция (без HTTP)
$session = ['user_id' => 42, 'name' => 'Алиса', 'role' => 'student'];
echo "Пользователь: {$session['name']} (роль: {$session['role']})\n";
echo "ID сессии: " . uniqid() . "\n";
?>

Обработка форм

<!DOCTYPE html>
<html lang="ru">
<head><meta charset="UTF-8"><title>Форма</title></head>
<body>
<?php
// Обработка отправленной формы
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $name  = trim($_POST['name']  ?? '');
    $email = trim($_POST['email'] ?? '');
    $age   = (int)($_POST['age']  ?? 0);

    $errors = [];
    if (empty($name)) $errors[] = "Имя обязательно";
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = "Неверный email";
    }
    if ($age < 5 || $age > 120) $errors[] = "Неверный возраст";

    if (empty($errors)) {
        $name  = htmlspecialchars($name);   // защита от XSS!
        $email = htmlspecialchars($email);
        echo "<p style='color:green'>✅ Привет, $name! Email: $email, Возраст: $age</p>";
    } else {
        foreach ($errors as $err) {
            echo "<p style='color:red'>❌ $err</p>";
        }
    }
}
?>
<form method="POST">
    <label>Имя: <input name="name" type="text"></label><br>
    <label>Email: <input name="email" type="email"></label><br>
    <label>Возраст: <input name="age" type="number"></label><br>
    <button type="submit">Отправить</button>
</form>
</body></html>

Подключение к MySQL через PDO

<?php
// PDO — современный способ работы с БД
try {
    $pdo = new PDO(
        "mysql:host=localhost;dbname=school;charset=utf8mb4",
        "username",
        "password",
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
    );

    // Создание таблицы
    $pdo->exec("CREATE TABLE IF NOT EXISTS students (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(100),
        age INT,
        gpa DECIMAL(3,2)
    )");

    // Вставка данных (prepared statement — защита от SQL-инъекций!)
    $stmt = $pdo->prepare("INSERT INTO students (name, age, gpa) VALUES (:name, :age, :gpa)");
    $students = [
        ["Алиса", 12, 4.8],
        ["Боря",  13, 3.9],
        ["Вера",  12, 4.5],
    ];
    foreach ($students as [$name, $age, $gpa]) {
        $stmt->execute([':name' => $name, ':age' => $age, ':gpa' => $gpa]);
    }

    // Выборка
    $rows = $pdo->query("SELECT * FROM students ORDER BY gpa DESC")->fetchAll(PDO::FETCH_ASSOC);
    foreach ($rows as $row) {
        echo "{$row['name']}: GPA {$row['gpa']}\n";
    }

    // Параметрический запрос
    $stmt = $pdo->prepare("SELECT * FROM students WHERE age = :age");
    $stmt->execute([':age' => 12]);
    $twelveyear = $stmt->fetchAll(PDO::FETCH_ASSOC);
    echo "\n12-летних: " . count($twelveyear) . "\n";

} catch (PDOException $e) {
    echo "Ошибка БД: " . $e->getMessage() . "\n";
}
?>