Dołączanie plików
Jeżeli nasz skrypt zawiera dużo kodu możemy go podzielić na mniejsze partie, a później „sklejać” w całość. Po co? Zobaczmy.
- Lepsze zarządzanie projektem
- Wielokrotne użycie kodu (DRY – Don’t Repeat Yourself)
- Łatwiejsze utrzymanie i aktualizacja
- Separacja odpowiedzialności
- Współpraca w zespole
Wiemy już jaki jest sens dzielenia pliku, ale jak to później połączyć? ano tak:
include()
dołącza plik do kodu, w razie braku pliku generuje ostrzeżenie kontynuując pracę skryptu
include('header.php');
include('functions.php');
// Skrypt działa dalej, nawet jeśli pliku nie ma
echo "Strona działa";
Kiedy używać:
- Pliki opcjonalne (np. moduły, które mogą nie istnieć)
- Elementy strony, których brak nie blokuje całości
include_once()
dołącza plik do kodu jednokrotnie, w razie braku pliku generuje ostrzeżenie kontynuując pracę skryptu
include_once('config.php');
include_once('config.php'); // Zostanie zignorowane
Po co:
- Zapobiega wielokrotnemu dołączeniu tego samego pliku
- Unika redefinicji funkcji i klas
require()
dołącza plik do kodu, w razie braku pliku generuje błąd i przerywa działanie skryptu
require('config.php');
require('database.php');
// Jeśli któryś plik nie istnieje, skrypt się zatrzyma
echo "Ta linia może się nie wykonać";
Kiedy używać:
- Pliki krytyczne (konfiguracja, baza danych)
- Elementy bez których aplikacja nie może działać
require_once()
dołącza plik do kodu jednokrotnie, w razie braku pliku generuje błąd i przerywa działanie skryptu
require_once('config.php');
require_once('config.php'); // Zostanie zignorowane
Najczęściej używane – łączy bezpieczeństwo require z ochroną przed wielokrotnym dołączeniem.
Porównanie funkcji
| Funkcja | Przy braku pliku | Wielokrotne dołączenie |
|---|---|---|
include |
Warning, kontynuuje | ✓ Dozwolone |
include_once |
Warning, kontynuuje | ✗ Tylko raz |
require |
Fatal Error, przerywa | ✓ Dozwolone |
require_once |
Fatal Error, przerywa | ✗ Tylko raz |
Składnia ścieżek
// Ścieżka względna - od bieżącego pliku
require_once('header.php');
require_once('./includes/config.php');
require_once('../parent-dir/file.php');
// Ścieżka bezwzględna - od roota serwera
require_once('/var/www/html/config.php');
// Ze zmienną __DIR__ (zalecane)
require_once(__DIR__ . '/includes/config.php');
// Przykład
$base = __DIR__;
require_once($base . '/config/database.php');
require_once($base . '/includes/functions.php');
Używaj __DIR__ dla pewności, że ścieżka jest poprawna niezależnie od miejsca wywołania.
Modularyzacja kodu
Przykład struktury projektu
projekt/
├── index.php
├── config/
│ ├── config.php
│ └── database.php
├── includes/
│ ├── header.php
│ ├── footer.php
│ └── menu.php
├── functions/
│ ├── auth.php
│ └── helpers.php
└── assets/
├── css/
├── js/
└── images/
Nasze pliki wyglądają teraz tak:
header.php
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $pageTitle ?? 'Moja Strona' ?></title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<header>
<h1>Moja Strona WWW</h1>
</header>
menu.php
<nav id="menu">
<ul>
<li><a href="index.php">Start</a></li>
<li><a href="about.php">O nas</a></li>
<li><a href="contact.php">Kontakt</a></li>
<li><a href="blog.php">Blog</a></li>
</ul>
</nav>
footer.php
<footer>
<p>© <?= date('Y') ?> Moja Strona. Wszelkie prawa zastrzeżone.</p>
</footer>
<script src="assets/js/main.js"></script>
</body>
</html>
index.php
<?php
// Konfiguracja
require_once(__DIR__ . '/config/config.php');
// Ustawienia strony
$pageTitle = 'Strona główna';
// Dołącz nagłówek i menu
require_once(__DIR__ . '/includes/header.php');
require_once(__DIR__ . '/includes/menu.php');
?>
<main>
<section>
<h2>Witamy na stronie głównej</h2>
<p>To jest treść strony głównej.</p>
</section>
</main>
<?php
// Dołącz stopkę
require_once(__DIR__ . '/includes/footer.php');
?>
config.php
<?php
// Konfiguracja aplikacji
define('SITE_NAME', 'Moja Strona');
define('SITE_URL', 'https://example.com');
define('SITE_EMAIL', 'kontakt@example.com');
// Ustawienia bazy danych
define('DB_HOST', 'localhost');
define('DB_NAME', 'mojabaza');
define('DB_USER', 'root');
define('DB_PASS', '');
// Strefa czasowa
date_default_timezone_set('Europe/Warsaw');
// Raportowanie błędów (development)
error_reporting(E_ALL);
ini_set('display_errors', 1);
Użycie:
<?php
require_once(__DIR__ . '/includes/Template.php');
$template = new Template();
$template->setTitle('Strona główna');
$template->addStyle('assets/css/style.css');
$template->addScript('assets/js/main.js');
$template->header();
?>
<h1>Treść strony</h1>
<?php
$template->footer();
?>
Operacje na katalogach
is_dir() / is_file()
Sprawdzają, czy ścieżka wskazuje na katalog lub plik.
$path = 'uploads';
if (is_dir($path)) {
echo "$path jest katalogiem";
}
if (is_file('config.php')) {
echo "config.php jest plikiem";
}
// Sprawdź przed operacją
if (is_dir('logs')) {
echo "Katalog logs istnieje";
} else {
mkdir('logs');
echo "Katalog logs został utworzony";
}
getcwd() / chdir()
Pobieranie i zmiana bieżącego katalogu roboczego.
// Bieżący katalog
echo getcwd(); // np. /var/www/html
// Zmień katalog
chdir('public_html');
echo getcwd(); // /var/www/html/public_html
// Powrót do poprzedniego
chdir('..');
echo getcwd(); // /var/www/html
scandir()
Wypisze wszystkie pliki i katalogi z określonej lokalizacji. Pierwszy parametr to lokalizacja katalogu, drugi to sortowanie: 0 (rosnąco – domyślny) lub 1(malejąco)
$dir = 'uploads';
$files = scandir($dir);
print_r($files);
/*
Array
(
[0] => .
[1] => ..
[2] => image1.jpg
[3] => document.pdf
[4] => subfolder
)
*/
// Sortowanie malejąco
$files = scandir($dir, SCANDIR_SORT_DESCENDING);
// Pomiń . i ..
$files = array_diff(scandir($dir), ['.', '..']);
// Wyświetl tylko pliki
foreach ($files as $file) {
$path = $dir . '/' . $file;
if (is_file($path)) {
echo "Plik: $file\n";
}
}
mkdir() i rmdir()
Tworzenie katalogu:
Przykład:
// Prosty katalog
if (mkdir('uploads')) {
echo "Katalog utworzony";
}
// Z prawami dostępu (Unix/Linux)
mkdir('cache', 0755);
// Rekurencyjne tworzenie (zagnieżdżone)
mkdir('a/b/c/d', 0755, true);
// Sprawdź czy istnieje przed utworzeniem
if (!is_dir('logs')) {
mkdir('logs', 0755, true);
}
Z parametrami (np. prawa dostępu i tworzenie zagnieżdżonych katalogów):
mkdir('a/b/c', 0777, true); // true → rekurencyjnie tworzy strukturę
Prawa dostępu (Unix/Linux):
0755– właściciel: rwx, grupa: r-x, inni: r-x0777– wszyscy: rwx (rzadko zalecane ze względów bezpieczeństwa)0700– tylko właściciel: rwx
Usuwanie katalogu:
Przykład:
// Usuń pusty katalog
if (rmdir('temp')) {
echo "Katalog usunięty";
}
// rmdir() działa TYLKO dla pustych katalogów!
// Usuń katalog z zawartością (rekurencyjnie)
function rmdirRecursive($dir) {
if (!is_dir($dir)) {
return false;
}
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = $dir . '/' . $file;
is_dir($path) ? rmdirRecursive($path) : unlink($path);
}
return rmdir($dir);
}
// Użycie
rmdirRecursive('old_folder');
rmdir() działa tylko, jeśli katalog jest pusty.
Funkcje ścieżek — basename(), dirname(), pathinfo(), realpath()
Służą do analizowania ścieżek bez potrzeby operowania na stringach ręcznie.
basename() — zwraca nazwę pliku
$path = '/var/www/html/index.php';
echo basename($path); // index.php
// Bez rozszerzenia
echo basename($path, '.php'); // index
dirname() — zwraca katalog nadrzędny
$path = '/var/www/html/index.php';
echo dirname($path); // /var/www/html
// Poziom wyżej
echo dirname($path, 2); // /var/www (PHP 7.0+)
pathinfo() — zwraca tablicę z częściami ścieżki
$path = '/var/www/html/images/photo.jpg';
$info = pathinfo($path);
print_r($info);
/*
Array
(
[dirname] => /var/www/html/images
[basename] => photo.jpg
[extension] => jpg
[filename] => photo
)
*/
// Konkretna część
echo pathinfo($path, PATHINFO_EXTENSION); // jpg
echo pathinfo($path, PATHINFO_FILENAME); // photo
echo pathinfo($path, PATHINFO_DIRNAME); // /var/www/html/images
echo pathinfo($path, PATHINFO_BASENAME); // photo.jpg
realpath() – bezwzględna ścieżka
// Bieżący katalog: /var/www/html
echo realpath('.'); // /var/www/html
echo realpath('..'); // /var/www
echo realpath('./config.php'); // /var/www/html/config.php
// Sprawdź czy plik istnieje
if (realpath('config.php') !== false) {
echo "Plik istnieje";
}
Nowoczesne stałe ścieżek (PHP 5.3+)
// __DIR__ - katalog bieżącego pliku
echo __DIR__; // /var/www/html
// __FILE__ - pełna ścieżka bieżącego pliku
echo __FILE__; // /var/www/html/index.php
// Zastosowanie
require_once(__DIR__ . '/config/database.php');
require_once(__DIR__ . '/../includes/functions.php');
// Bezpieczne budowanie ścieżek
$configPath = __DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'app.php';
Otwieranie katalogów — opendir(), readdir(), closedir()
Służy do przeglądania zawartości katalogów, np. listowania plików.
$dir = 'uploads';
if ($handle = opendir($dir)) {
echo "Zawartość katalogu $dir:<br>";
while (($file = readdir($handle)) !== false) {
// Pomiń . i ..
if ($file != '.' && $file != '..') {
$type = is_dir("$dir/$file") ? 'katalog' : 'plik';
echo "• $file ($type)<br>";
}
}
closedir($handle);
}
Nowoczesna alternatywa – scandir():
$dir = 'uploads';
$files = array_diff(scandir($dir), ['.', '..']);
foreach ($files as $file) {
$path = "$dir/$file";
$type = is_dir($path) ? 'katalog' : 'plik';
echo "• $file ($type)<br>";
}
Podsumowanie
| Operacja | Funkcja | Uwagi |
|---|---|---|
| Blokowanie pliku | flock() | Zapobiega konfliktom przy zapisie |
| Kopiowanie | copy() | Nadpisuje, jeśli plik istnieje |
| Przenoszenie / zmiana nazwy | rename() | Działa dla plików i katalogów |
| Usuwanie pliku | unlink() | Bez kosza |
| Tworzenie katalogu | mkdir() | Obsługuje tryby i zagnieżdżenie |
| Usuwanie katalogu | rmdir() | Katalog musi być pusty |
| Analiza ścieżek | basename(), dirname(), pathinfo() | Bardzo pomocne przy pracy z uploadem |
| Przeglądanie katalogów | opendir(), readdir(), closedir() | Iteracja po plikach |
Nowoczesne podejście
SPL DirectoryIterator (PHP 5.0+)
$dir = 'uploads';
foreach (new DirectoryIterator($dir) as $file) {
if ($file->isDot()) continue; // pomiń . i ..
echo $file->getFilename();
if ($file->isDir()) {
echo " [katalog]";
} elseif ($file->isFile()) {
echo " [plik, " . $file->getSize() . " bajtów]";
}
echo "\n";
}
RecursiveDirectoryIterator – przeglądanie rekurencyjne
$dir = 'project';
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $file) {
if ($file->isFile()) {
echo $file->getPathname() . "\n";
}
}
FilesystemIterator – więcej opcji
$dir = 'uploads';
$iterator = new FilesystemIterator($dir,
FilesystemIterator::SKIP_DOTS // pomiń . i ..
);
foreach ($iterator as $file) {
echo $file->getFilename() . "\n";
}
Glob – wyszukiwanie wzorców
// Wszystkie pliki PHP
$phpFiles = glob('*.php');
print_r($phpFiles);
// Wszystkie obrazy
$images = glob('images/*.{jpg,jpeg,png,gif}', GLOB_BRACE);
// Rekurencyjne wyszukiwanie
$allPhp = glob('**/*.php', GLOB_BRACE);
// Przykład: usuń wszystkie pliki tymczasowe
foreach (glob('temp/*.tmp') as $file) {
unlink($file);
}
Tabela funkcji – szybka ściąga
| Operacja | Funkcja | Opis |
|---|---|---|
| Dołączanie | ||
include() | Dołącz, warning przy błędzie | |
include_once() | Dołącz raz, warning | |
require() | Dołącz, fatal error przy błędzie | |
require_once() | Dołącz raz, fatal error | |
| Katalogi | ||
is_dir() | Czy to katalog? | |
mkdir() | Utwórz katalog | |
rmdir() | Usuń pusty katalog | |
scandir() | Lista plików/katalogów | |
getcwd() | Bieżący katalog | |
chdir() | Zmień katalog | |
| Ścieżki | ||
basename() | Nazwa pliku | |
dirname() | Katalog nadrzędny | |
pathinfo() | Szczegóły ścieżki | |
realpath() | Bezwzględna ścieżka |
Dobre praktyki
1. Zawsze używaj require_once dla plików krytycznych
// ŹLE
include('config.php');
// DOBRZE
require_once(__DIR__ . '/config/config.php');
2. Używaj DIR zamiast względnych ścieżek
// ŹLE - może nie działać
require_once('../includes/header.php');
// DOBRZE - zawsze działa
require_once(__DIR__ . '/../includes/header.php');
3. Sprawdzaj istnienie katalogów przed operacjami
// ŹLE
mkdir('cache');
// DOBRZE
if (!is_dir('cache')) {
mkdir('cache', 0755, true);
}
4. Używaj separatora katalogów
// ŹLE - nie działa na Windows
$path = '/var/www/html/config.php';
// DOBRZE - uniwersalne
$path = __DIR__ . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR . 'config.php';
// Lub po prostu /
$path = __DIR__ . '/config/config.php'; // działa na wszystkich systemach
5. Nigdy nie ufaj danym użytkownika w ścieżkach
// ŹLE - podatne na Path Traversal
$file = $_GET['file'];
include("pages/$file.php"); // Użytkownik może wpisać: ../../config/database
// DOBRZE - walidacja
$file = basename($_GET['file']); // usuwa ścieżki
$allowed = ['home', 'about', 'contact'];
if (in_array($file, $allowed)) {
include("pages/$file.php");
}
Przykłady praktyczne
1. Prosty system szablonów
functions/template.php:
<?php
function renderTemplate(string $templateName, array $data = []): void {
$templatePath = __DIR__ . '/../templates/' . $templateName . '.php';
if (!file_exists($templatePath)) {
throw new Exception("Template not found: $templateName");
}
// Wyciągnij zmienne z tablicy
extract($data);
// Dołącz szablon
require($templatePath);
}
templates/page.php:
<!DOCTYPE html>
<html lang="pl">
<head>
<title><?= htmlspecialchars($title ?? 'Strona') ?></title>
</head>
<body>
<h1><?= htmlspecialchars($heading) ?></h1>
<p><?= htmlspecialchars($content) ?></p>
</body>
</html>
index.php:
<?php
require_once(__DIR__ . '/functions/template.php');
renderTemplate('page', [
'title' => 'Moja Strona',
'heading' => 'Witamy',
'content' => 'To jest treść strony.'
]);
2. Automatyczne ładowanie plików (Autoloader)
<?php
// autoload.php
spl_autoload_register(function($className) {
$baseDir = __DIR__ . '/classes/';
$file = $baseDir . str_replace('\\', '/', $className) . '.php';
if (file_exists($file)) {
require_once($file);
}
});
// Użycie
require_once('autoload.php');
$user = new User(); // automatycznie załaduje classes/User.php
$db = new Database(); // automatycznie załaduje classes/Database.php
3. Galeria zdjęć z katalogu
<?php
function displayGallery(string $dir): void {
$extensions = ['jpg', 'jpeg', 'png', 'gif'];
$images = [];
foreach ($extensions as $ext) {
$images = array_merge($images, glob("$dir/*.$ext"));
}
if (empty($images)) {
echo "<p>Brak zdjęć w galerii.</p>";
return;
}
echo '<div class="gallery">';
foreach ($images as $image) {
$filename = basename($image);
echo '<div class="gallery-item">';
echo "<img src='$image' alt='$filename' loading='lazy'>";
echo "<p>$filename</p>";
echo '</div>';
}
echo '</div>';
}
// Użycie
displayGallery('images/gallery');
?>
<style>
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 1rem;
}
.gallery-item img {
width: 100%;
height: 200px;
object-fit: cover;
}
</style>
4. Lista plików do pobrania
<?php
function listDownloads(string $dir): void {
if (!is_dir($dir)) {
echo "Katalog nie istnieje.";
return;
}
$files = array_diff(scandir($dir), ['.', '..']);
if (empty($files)) {
echo "<p>Brak plików do pobrania.</p>";
return;
}
echo '<ul class="downloads">';
foreach ($files as $file) {
$path = "$dir/$file";
if (is_file($path)) {
$size = filesize($path);
$sizeFormatted = formatBytes($size);
$extension = pathinfo($path, PATHINFO_EXTENSION);
echo '<li>';
echo "<a href='$path' download>";
echo "<strong>$file</strong> ($sizeFormatted, .$extension)";
echo '</a>';
echo '</li>';
}
}
echo '</ul>';
}
function formatBytes(int $bytes, int $precision = 2): string {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
for ($i = 0; $bytes > 1024; $i++) {
$bytes /= 1024;
}
return round($bytes, $precision) . ' ' . $units[$i];
}
// Użycie
listDownloads('downloads');
5. Cache systemu
<?php
class FileCache {
private string $cacheDir;
private int $ttl; // Time To Live w sekundach
public function __construct(string $cacheDir = 'cache', int $ttl = 3600) {
$this->cacheDir = $cacheDir;
$this->ttl = $ttl;
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
}
public function get(string $key): mixed {
$file = $this->getCacheFile($key);
if (!file_exists($file)) {
return null;
}
// Sprawdź czy nie wygasł
if (time() - filemtime($file) > $this->ttl) {
unlink($file);
return null;
}
$content = file_get_contents($file);
return unserialize($content);
}
public function set(string $key, mixed $value): bool {
$file = $this->getCacheFile($key);
$content = serialize($value);
return file_put_contents($file, $content) !== false;
}
public function delete(string $key): bool {
$file = $this->getCacheFile($key);
if (file_exists($file)) {
return unlink($file);
}
return false;
}
public function clear(): void {
$files = glob($this->cacheDir . '/*');
foreach ($files as $file) {
if (is_file($file)) {
unlink($file);
}
}
}
private function getCacheFile(string $key): string {
return $this->cacheDir . '/' . md5($key) . '.cache';
}
}
// Użycie
$cache = new FileCache('cache', 3600); // 1 godzina TTL
// Zapisz
$cache->set('users', ['Jan', 'Anna', 'Piotr']);
// Odczytaj
$users = $cache->get('users');
// Usuń
$cache->delete('users');
// Wyczyść wszystko
$cache->clear();
Zadanie dla klas, które miały HTMLa:
Rozpocznij tworzenie autorskiego CMS – możesz wykorzystać do tego stronę www, którą posiadasz (nie WordPress, Joomla itp.).
Wydziel z niej bloki odpowiedzialne za formatowanie tych części strony, które się nie zmieniają (jak w przykładzie wyżej) tzn.:
- nagłówek
- menu
- stopka
- część główna strony
- panele boczne
- itp.
