Dobre praktyki pisania kodu PHP
Wprowadzenie
Dział zatytułowany „Wprowadzenie”Dobre praktyki programistyczne to sprawdzone zasady, które pomagaja pisac kod łatwiejszy do zrozumienia, utrzymania i rozwijania. W PHP szczegolnie ważne jest przestrzeganie tych zasad, ponieważ język pozwala na wiele “skrotow”, które prowadza do trudnego w utrzymaniu kodu.
Czysty kod to nie tylko estetyka - to oszczednosc czasu, mniej błędów i lepsza współpraca w zespole.
Kluczowe pojecia
Dział zatytułowany „Kluczowe pojecia”Clean Code
Dział zatytułowany „Clean Code”Kod, który jest łatwy do zrozumienia i modyfikacji. Każda funkcja robi jedna rzecz, nazwy sa opisowe, a struktura jest przejrzysta.
DRY (Don’t Repeat Yourself)
Dział zatytułowany „DRY (Don’t Repeat Yourself)”Nie powtarzaj kodu - jeśli cos piszesz dwa razy, zrob z tego funkcje.
KISS (Keep It Simple, Stupid)
Dział zatytułowany „KISS (Keep It Simple, Stupid)”Proste rozwiązania sa lepsze od skomplikowanych.
Piec zasad projektowania obiektowego (dla zaawansowanych).
Czytelnosc kodu
Dział zatytułowany „Czytelnosc kodu”Nazewnictwo zmiennych
Dział zatytułowany „Nazewnictwo zmiennych”<?php// ZLE - nieczytelne nazwy$x = "Jan Kowalski";$a = 25;$arr = [];
// DOBRZE - opisowe nazwy$userName = "Jan Kowalski";$userAge = 25;$orderItems = [];
// ZLE - skroty$usrNm = "Jan";$prc = 99.99;
// DOBRZE - pełne słowa$userName = "Jan";$price = 99.99;Konwencje nazewnictwa
Dział zatytułowany „Konwencje nazewnictwa”| Element | Konwencja | Przykład |
|---|---|---|
| Zmienne | camelCase | $userName, $orderTotal |
| Funkcje | camelCase | getUserById(), calculateTotal() |
| Klasy | PascalCase | UserController, OrderService |
| Stałe | UPPER_SNAKE | MAX_LOGIN_ATTEMPTS, DB_HOST |
Formatowanie kodu
Dział zatytułowany „Formatowanie kodu”<?php// ZLE - brak wciec, nieczytelnefunction processOrder($order){if($order->status=='pending'){$total=$order->calculateTotal();if($total>100){$order->applyDiscount(10);}$order->save();return true;}return false;}
// DOBRZE - czytelne formatowaniefunction processOrder(Order $order): bool{ if ($order->status !== 'pending') { return false; }
$total = $order->calculateTotal();
if ($total > 100) { $order->applyDiscount(10); }
$order->save();
return true;}Zasada DRY - nie powtarzaj kodu
Dział zatytułowany „Zasada DRY - nie powtarzaj kodu”<?php// ZLE - powtorzony kodfunction getActiveUsers($pdo) { $stmt = $pdo->prepare("SELECT * FROM users WHERE status = 'active'"); $stmt->execute(); return $stmt->fetchAll();}
function getInactiveUsers($pdo) { $stmt = $pdo->prepare("SELECT * FROM users WHERE status = 'inactive'"); $stmt->execute(); return $stmt->fetchAll();}
function getPendingUsers($pdo) { $stmt = $pdo->prepare("SELECT * FROM users WHERE status = 'pending'"); $stmt->execute(); return $stmt->fetchAll();}
// DOBRZE - jedna funkcja z parametremfunction getUsersByStatus(PDO $pdo, string $status): array{ $stmt = $pdo->prepare("SELECT * FROM users WHERE status = ?"); $stmt->execute([$status]); return $stmt->fetchAll();}
// Użycie$activeUsers = getUsersByStatus($pdo, 'active');$inactiveUsers = getUsersByStatus($pdo, 'inactive');Jedna funkcja - jedno zadanie
Dział zatytułowany „Jedna funkcja - jedno zadanie”<?php// ZLE - funkcja robi za dużofunction processUserRegistration($data) { // Walidacja if (empty($data['email'])) { return ['error' => 'Email required']; } if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { return ['error' => 'Invalid email']; } if (strlen($data['password']) < 8) { return ['error' => 'Password too short']; }
// Zapis do bazy $hash = password_hash($data['password'], PASSWORD_DEFAULT); $stmt = $pdo->prepare("INSERT INTO users ..."); $stmt->execute([...]);
// Wysłanie e-maila mail($data['email'], 'Welcome!', '...');
return ['success' => true];}
// DOBRZE - podział na mniejsze funkcjefunction validateUserData(array $data): ?string{ if (empty($data['email'])) { return 'Email wymagany'; } if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { return 'Nieprawidłowy email'; } if (strlen($data['password']) < 8) { return 'Hasło za krótkie'; } return null; // Brak błędów}
function createUser(PDO $pdo, array $data): int{ $hash = password_hash($data['password'], PASSWORD_DEFAULT); $stmt = $pdo->prepare("INSERT INTO users (email, password) VALUES (?, ?)"); $stmt->execute([$data['email'], $hash]); return $pdo->lastInsertId();}
function sendWelcomeEmail(string $email): void{ mail($email, 'Witamy!', 'Dziekujemy za rejestracje.');}
// Użycie - czytelny przepływ$error = validateUserData($data);if ($error) { return ['error' => $error];}
$userId = createUser($pdo, $data);sendWelcomeEmail($data['email']);Bezpieczeństwo
Dział zatytułowany „Bezpieczeństwo”Prepared Statements (SQL Injection)
Dział zatytułowany „Prepared Statements (SQL Injection)”<?php// ZLE - podatne na SQL Injection$username = $_POST['username'];$query = "SELECT * FROM users WHERE username = '$username'";$result = $pdo->query($query);
// DOBRZE - prepared statement$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?");$stmt->execute([$_POST['username']]);$user = $stmt->fetch();Escapowanie wyjscia (XSS)
Dział zatytułowany „Escapowanie wyjscia (XSS)”<?php// ZLE - podatne na XSSecho "Witaj, " . $_GET['name'];
// DOBRZE - escapowanieecho "Witaj, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');Walidacja danych wejsciowych
Dział zatytułowany „Walidacja danych wejsciowych”<?php// DOBRZE - walidacja przed użyciem$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);$age = filter_input(INPUT_POST, 'age', FILTER_VALIDATE_INT);
if (!$email) { die('Nieprawidłowy email');}
if ($age === false || $age < 0 || $age > 150) { die('Nieprawidłowy wiek');}Obsługa błędów
Dział zatytułowany „Obsługa błędów”<?php// ZLE - ukrywanie błędów$result = @file_get_contents('plik.txt');
// ZLE - generyczne komunikatytry { // kod} catch (Exception $e) { echo "Błąd";}
// DOBRZE - właściwa obsługatry { $content = file_get_contents('plik.txt'); if ($content === false) { throw new RuntimeException('Nie można odczytać pliku'); }} catch (RuntimeException $e) { error_log($e->getMessage()); // Loguj błąd echo "Przepraszamy, wystąpił problem."; // User-friendly komunikat}Type Hints i Return Types
Dział zatytułowany „Type Hints i Return Types”<?php// ZLE - brak typowfunction calculateDiscount($price, $percentage) { return $price * ($percentage / 100);}
// DOBRZE - typy i dokumentacja/** * Oblicza rabat od ceny * * @param float $price Cena produktu * @param float $percentage Procent rabatu (0-100) * @return float Wartość rabatu */function calculateDiscount(float $price, float $percentage): float{ if ($percentage < 0 || $percentage > 100) { throw new InvalidArgumentException('Percentage must be 0-100'); }
return $price * ($percentage / 100);}Przykład refaktoryzacji
Dział zatytułowany „Przykład refaktoryzacji”Przed (zły kod)
Dział zatytułowany „Przed (zły kod)”<?php// Nieczytelny, powtarzajacy się, niebezpieczny kod$u = $_POST['u'];$p = $_POST['p'];$q = "SELECT * FROM users WHERE username='$u'";$r = mysqli_query($conn, $q);$row = mysqli_fetch_assoc($r);if($row['password'] == $p) { $_SESSION['logged'] = true; $_SESSION['user'] = $row['username']; header('Location: dashboard.php');} else { echo "Źle dane";}Po (dobry kod)
Dział zatytułowany „Po (dobry kod)”<?phpdeclare(strict_types=1);
function authenticateUser(PDO $pdo, string $username, string $password): ?array{ $stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?"); $stmt->execute([$username]); $user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user || !password_verify($password, $user['password_hash'])) { return null; }
return $user;}
function loginUser(array $user): void{ session_regenerate_id(true); $_SESSION['user_id'] = $user['id']; $_SESSION['username'] = $user['username'];}
// Użycie$username = $_POST['username'] ?? '';$password = $_POST['password'] ?? '';
$user = authenticateUser($pdo, $username, $password);
if ($user) { loginUser($user); header('Location: dashboard.php'); exit;}
$error = 'Nieprawidłowy login lub hasło';Najczestsze błędy
Dział zatytułowany „Najczestsze błędy”Podsumowanie
Dział zatytułowany „Podsumowanie”- Uzywaj opisowych nazw zmiennych i funkcji
- Przestrzegaj zasady DRY - nie powtarzaj kodu
- Jedna funkcja = jedno zadanie
- Zawsze uzywaj prepared statements (SQL)
- Escapuj dane wyjsciowe (XSS)
- Dodawaj type hints i return types
- Obsługuj błędy właściwie
- Formatuj kod konsekwentnie
PHP The Right Way Kompendium dobrych praktyk PHP
PSR Standards Standardy kodowania PHP
Clean Code (książka) Klasyka o czystym kodzie