W budowie
System plików, dzienniki zdarzeń, format CSV oraz bezpieczny upload plików graficznych

Część 19 · Operacje na plikach i przesyłanie plików na serwer — kurs PHP INF.03

Część 19 kursu PHP pod kątem INF.03: operacje na plikach (odczyt, zapis, CSV, raporty) oraz przesyłanie plików formularzem na serwer ($_FILES, move_uploaded_file, walidacja rozszerzenia). 16 ćwiczeń z automatycznym sprawdzaniem.

  • 45 min
  • 16 slajdów
  • Notatki nauczyciela: N
  • Strona modułu: otwórz
Plan · 45 min

Co dziś robimy

  1. 12 minOdczyt i zapis plików Przy dużych plikach lepsze są fopen() , fgets() i fclose() , które czytają linia po linii.
  2. 12 minPrzesyłanie pliku na serwer Dane trafiają do tablicy $_FILES z kluczami name , tmp_name , size i error .
  3. 12 minBezpieczeństwo plików i katalogów Nazwę pliku budujemy sami i nie ufamy $_FILES['name'] .
  4. 4 minĆwiczenia 16 zadań w module — wybierz 3–4 na lekcję
  5. 5 minPodsumowanie bilet wyjściowy i zadanie domowe
Atom 1 z 4 · ~12 min

Odczyt i zapis plików

Do pracy z plikami służą file_get_contents() (odczyt całości) i file_put_contents() (zapis). Są proste, ale wczytują cały plik do pamięci.

  • Przy dużych plikach lepsze są fopen() , fgets() i fclose() , które czytają linia po linii.
  • Tryb otwarcia określa, czy plik ma być czytany, dopisywany czy nadpisany.
  • Każda operacja na plikach wymaga sprawdzenia wyniku.
  • Brak praw zapisu albo nieistniejący katalog to najczęstsze przyczyny błędów, a nie błędy w samym kodzie.
Kod · atom 1 · ~3 min

Jak to wygląda w kodzie

$sciezka = __DIR__ . '/dane/log.txt';

file_put_contents($sciezka, date('Y-m-d H:i') . " start\n", FILE_APPEND);

if (is_file($sciezka)) {
    $linie = file($sciezka, FILE_IGNORE_NEW_LINES);
    foreach ($linie as $linia) {
        echo '<p>' . htmlspecialchars($linia) . '</p>';
    }
}
Przykład na żywo · atom 1 · ~3 min

Uruchom i pokaż

$sciezka = __DIR__ . '/dane/log.txt';

file_put_contents($sciezka, date('Y-m-d H:i') . " start\n", FILE_APPEND);

if (is_file($sciezka)) {
    $linie = file($sciezka, FILE_IGNORE_NEW_LINES);
    foreach ($linie as $linia) {
        echo '<p>' . htmlspecialchars($linia) . '</p>';
    }
}
Sprawdzenie · atom 1 · ~2 min

Pytanie do klasy

Co robi file_get_contents()?

Atom 2 z 4 · ~12 min

Przesyłanie pliku na serwer

Formularz z plikiem wymaga method="post" i atrybutu enctype="multipart/form-data" . Bez enctype przeglądarka wyśle tylko nazwę pliku.

  • Dane trafiają do tablicy $_FILES z kluczami name , tmp_name , size i error .
  • Plik trzeba przenieść z katalogu tymczasowego funkcją move_uploaded_file() .
  • Zawsze sprawdzamy kod błędu, rozmiar i rozszerzenie.
  • Nazwę pliku generujemy sami, żeby uniknąć nadpisania i dziwnych znaków w adresie.
Kod · atom 2 · ~3 min

Jak to wygląda w kodzie

if (($_FILES['plik']['error'] ?? 1) === UPLOAD_ERR_OK) {
    $nazwa = basename($_FILES['plik']['name']);
    $koncowka = strtolower(pathinfo($nazwa, PATHINFO_EXTENSION));

    if (in_array($koncowka, ['jpg', 'png'], true) && $_FILES['plik']['size'] < 2000000) {
        $nowa = uniqid('img_', true) . '.' . $koncowka;
        move_uploaded_file($_FILES['plik']['tmp_name'], __DIR__ . '/upload/' . $nowa);
        echo 'Zapisano: ' . htmlspecialchars($nowa);
    } else {
        echo 'Nieprawidlowy plik';
    }
}
Przykład na żywo · atom 2 · ~3 min

Uruchom i pokaż

if (($_FILES['plik']['error'] ?? 1) === UPLOAD_ERR_OK) {
    $nazwa = basename($_FILES['plik']['name']);
    $koncowka = strtolower(pathinfo($nazwa, PATHINFO_EXTENSION));

    if (in_array($koncowka, ['jpg', 'png'], true) && $_FILES['plik']['size'] < 2000000) {
        $nowa = uniqid('img_', true) . '.' . $koncowka;
        move_uploaded_file($_FILES['plik']['tmp_name'], __DIR__ . '/upload/' . $nowa);
        echo 'Zapisano: ' . htmlspecialchars($nowa);
    } else {
        echo 'Nieprawidlowy plik';
    }
}
Sprawdzenie · atom 2 · ~2 min

Pytanie do klasy

Czego wymaga formularz z plikiem?

Atom 3 z 4 · ~12 min

Bezpieczeństwo plików i katalogów

Największe ryzyko to wykonanie przesłanego pliku. Jeśli użytkownik wgra plik .php do katalogu publicznego, serwer może go uruchomić. Dlatego sprawdzamy rozszerzenie i zapisujemy pliki poza katalogiem z kodem.

  • Nazwę pliku budujemy sami i nie ufamy $_FILES['name'] .
  • Znaki ../ w nazwie mogą wskazać inny katalog — to tak zwana path traversal.
  • Dodatkowo ograniczamy rozmiar i typ MIME, a katalogowi uploadu odbieramy prawo wykonywania skryptów.
  • To kilka linii konfiguracji, które zamykają najczęstszą lukę.
Kod · atom 3 · ~3 min

Jak to wygląda w kodzie

$dozwolone = ['jpg' => 'image/jpeg', 'png' => 'image/png'];
$rozszerzenie = strtolower(pathinfo($_FILES['plik']['name'] ?? '', PATHINFO_EXTENSION));
$typ = $_FILES['plik']['type'] ?? '';

if (!isset($dozwolone[$rozszerzenie]) || $dozwolone[$rozszerzenie] !== $typ) {
    exit('Niedozwolony typ pliku');
}

$nazwa = bin2hex(random_bytes(8)) . '.' . $rozszerzenie;
move_uploaded_file($_FILES['plik']['tmp_name'], __DIR__ . '/upload/' . $nazwa);
Przykład na żywo · atom 3 · ~3 min

Uruchom i pokaż

$dozwolone = ['jpg' => 'image/jpeg', 'png' => 'image/png'];
$rozszerzenie = strtolower(pathinfo($_FILES['plik']['name'] ?? '', PATHINFO_EXTENSION));
$typ = $_FILES['plik']['type'] ?? '';

if (!isset($dozwolone[$rozszerzenie]) || $dozwolone[$rozszerzenie] !== $typ) {
    exit('Niedozwolony typ pliku');
}

$nazwa = bin2hex(random_bytes(8)) . '.' . $rozszerzenie;
move_uploaded_file($_FILES['plik']['tmp_name'], __DIR__ . '/upload/' . $nazwa);
Sprawdzenie · atom 3 · ~2 min

Pytanie do klasy

Dlaczego sprawdzamy rozszerzenie?

Zamknięcie · ~3 min

Co zabrać z lekcji

  • Przy dużych plikach lepsze są fopen() , fgets() i fclose() , które czytają linia po linii.
  • Zawsze sprawdzamy kod błędu, rozmiar i rozszerzenie.
  • Największe ryzyko to wykonanie przesłanego pliku.
  • Ćwiczenia
Bilet wyjściowy: Skąd brać nazwę pliku?
Odpowiedź: d) Wygenerować samodzielnie
Hyżyk· Część 19 · Operacje na plikach i przesyłanie plików na serwe moduł 1 / 1