<?php
/**
* ========================================
* PHP/OOP Best Practices & Patterns
* ========================================
*/
// ============================================================================
// 1. ✅ KONSTANTY MÍSTO "MAGIC STRINGS"
// ============================================================================
// ❌ ŠPATNĚ - Magic strings, překlepy nenajdeš
if ($order->state === 'pending') { }
if ($order->state === 'pendig') { } // Překlep!
// ✅ DOBŘE - Konstanty, IDE ti pomůže
class OrderState
{
const PENDING = 'pending';
const SHIPPED = 'shipped';
const DELIVERED = 'delivered';
}
if ($order->state === OrderState::PENDING) { }
if ($order->state === OrderState::PENDIG) { } // IDE error!
// ============================================================================
// 2. 🎯 ENUM PATTERN (PHP 7.4) - Rozšířené konstanty
// ============================================================================
class OrderStateEnum
{
const PENDING = 1;
const SHIPPED = 2;
const DELIVERED = 3;
private static $names = [
self::PENDING => 'Čeká na zpracování',
self::SHIPPED => 'Odesláno',
self::DELIVERED => 'Doručeno',
];
private static $colors = [
self::PENDING => 'orange',
self::SHIPPED => 'blue',
self::DELIVERED => 'green',
];
public static function getName(int $state): string
{
return self::$names[$state] ?? 'Neznámý stav';
}
public static function getColor(int $state): string
{
return self::$colors[$state] ?? 'gray';
}
public static function getAll(): array
{
return self::$names;
}
}
// Použití:
$stateName = OrderStateEnum::getName(OrderStateEnum::PENDING); // "Čeká na zpracování"
$color = OrderStateEnum::getColor(OrderStateEnum::PENDING); // "orange"
// ============================================================================
// 3. 🚀 VALUE OBJECTS - Type Safety & Zapouzdření Logiky
// ============================================================================
// PŘÍKLAD 1: Money - Nemůžeš sčítat různé měny
// ❌ ŠPATNĚ
function calculateTotal(float $priceEur, float $priceCzk): float
{
return $priceEur + $priceCzk; // Sčítáš EUR + CZK?? 😱
}
$total = calculateTotal(100, 2500); // 2600 čeho??
// ✅ DOBŘE - Value Object
class Money
{
private float $amount;
private string $currency;
public function __construct(float $amount, string $currency)
{
$this->amount = $amount;
$this->currency = $currency;
}
public function add(Money $other): Money
{
if ($this->currency !== $other->currency) {
throw new \InvalidArgumentException(
"Cannot add {$other->currency} to {$this->currency}"
);
}
return new Money($this->amount + $other->amount, $this->currency);
}
public function multiply(float $multiplier): Money
{
return new Money($this->amount * $multiplier, $this->currency);
}
public function getAmount(): float { return $this->amount; }
public function getCurrency(): string { return $this->currency; }
}
// Použití:
$priceEur = new Money(100, 'EUR');
$priceCzk = new Money(2500, 'CZK');
$total = $priceEur->add($priceCzk); // ❌ Exception! Nelze EUR + CZK
$discount = $priceEur->multiply(0.9); // ✅ 90 EUR
// PŘÍKLAD 2: Percentage - Validace 0-100
// ❌ ŠPATNĚ
function applyDiscount(float $price, int $percent): float
{
return $price - ($price * $percent / 100);
}
$final = applyDiscount(1000, 150); // 150% sleva?? -500 Kč? 😱
// ✅ DOBŘE - Value Object
class Percentage
{
private int $value;
public function __construct(int $value)
{
if ($value < 0 || $value > 100) {
throw new \InvalidArgumentException(
"Percentage must be 0-100, got: $value"
);
}
$this->value = $value;
}
public function getValue(): int { return $this->value; }
public function applyTo(float $amount): float
{
return $amount * ($this->value / 100);
}
}
function applyDiscountSafe(float $price, Percentage $percent): float
{
return $price - $percent->applyTo($price);
}
$final = applyDiscountSafe(1000, new Percentage(150)); // ❌ Exception!
$final = applyDiscountSafe(1000, new Percentage(20)); // ✅ 800 Kč
// PŘÍKLAD 3: OrderState Value Object - Jen platné stavy
class OrderStateVO
{
private int $value;
private const NEW = 1;
private const PAID = 2;
private const SHIPPED = 3;
private static $valid = [self::NEW, self::PAID, self::SHIPPED];
private function __construct(int $value)
{
if (!in_array($value, self::$valid)) {
throw new \InvalidArgumentException("Invalid state: $value");
}
$this->value = $value;
}
public static function new(): self { return new self(self::NEW); }
public static function paid(): self { return new self(self::PAID); }
public static function shipped(): self { return new self(self::SHIPPED); }
public function isNew(): bool { return $this->value === self::NEW; }
public function isPaid(): bool { return $this->value === self::PAID; }
public function getValue(): int { return $this->value; }
}
// Použití:
$state = OrderStateVO::paid(); // ✅ Jen platné stavy!
$state = new OrderStateVO(999); // ❌ Private constructor - nelze!
if ($state->isPaid()) { /* Ship order */ }
// ============================================================================
// 4. 🛡️ NULL OBJECT PATTERN - Žádné null checks
// ============================================================================
// ❌ ŠPATNĚ - Null checks všude
$user = $userRepo->find($id);
if ($user !== null) {
echo $user->getName();
} else {
echo 'Guest';
}
// ✅ DOBŘE - Null Object
interface UserInterface
{
public function getName(): string;
public function isGuest(): bool;
}
class User implements UserInterface
{
private string $name;
public function __construct(string $name)
{
$this->name = $name;
}
public function getName(): string { return $this->name; }
public function isGuest(): bool { return false; }
}
class GuestUser implements UserInterface
{
public function getName(): string { return 'Guest'; }
public function isGuest(): bool { return true; }
}
// Použití:
$user = $userRepo->find($id) ?? new GuestUser();
echo $user->getName(); // Vždy funguje, žádný null check!
// ============================================================================
// 5. 🎨 EARLY RETURN - Guard Clauses místo vnořených if
// ============================================================================
// ❌ ŠPATNĚ - Pyramid of Doom
function processOrder($data)
{
if ($data !== null) {
if ($data->isValid()) {
if ($data->hasPermission()) {
return $data->process();
} else {
return 'No permission';
}
} else {
return 'Invalid';
}
} else {
return 'No data';
}
}
// ✅ DOBŘE - Guard Clauses (Early Return)
function processOrderClean($data)
{
if ($data === null) {
return 'No data';
}
if (!$data->isValid()) {
return 'Invalid';
}
if (!$data->hasPermission()) {
return 'No permission';
}
return $data->process();
}
// ============================================================================
// 6. 🔒 IMMUTABLE OBJECTS - Objekty se nemění
// ============================================================================
// ❌ ŠPATNĚ - Mutable (někdo změní a nevíš kde)
class MutablePrice
{
public float $value;
public function __construct(float $value)
{
$this->value = $value;
}
public function setValue(float $value)
{
$this->value = $value;
}
}
$price = new MutablePrice(100);
someFunction($price); // Co se stalo uvnitř?
echo $price->value; // Kdo ví... možná 100, možná 200?
// ✅ DOBŘE - Immutable (vrací nové instance)
class ImmutablePrice
{
private float $value;
public function __construct(float $value)
{
$this->value = $value;
}
public function getValue(): float { return $this->value; }
public function withDiscount(int $percent): self
{
return new self($this->value * (100 - $percent) / 100);
}
public function withTax(float $taxRate): self
{
return new self($this->value * (1 + $taxRate));
}
}
// Použití:
$price = new ImmutablePrice(100);
$discounted = $price->withDiscount(10); // Nový objekt
$withTax = $discounted->withTax(0.21); // Další nový objekt
echo $price->getValue(); // Stále 100! Původní nezměněn
echo $discounted->getValue(); // 90
echo $withTax->getValue(); // 108.9
// ============================================================================
// 7. 🎯 TYPE HINTS VŠUDE (PHP 7.4)
// ============================================================================
// ❌ ŠPATNĚ - Bez type hints
function calculate($a, $b)
{
return $a + $b;
}
calculate('10', '20'); // "1020" (string concatenation) 😱
calculate([1, 2], [3, 4]); // Fatal error až za běhu
// ✅ DOBŘE - S type hints
function calculateSafe(int $a, int $b): int
{
return $a + $b;
}
calculateSafe('10', '20'); // TypeError hned! ✅
calculateSafe(10, 20); // 30 ✅
// ============================================================================
// 8. 📦 DEPENDENCY INJECTION místo new
// ============================================================================
// ❌ ŠPATNĚ - Tight Coupling (pevné závislosti)
class OrderService
{
public function process()
{
$mailer = new Mailer(); // ❌ Nelze testovat, nelze změnit
$logger = new Logger(); // ❌ Pevně spojené třídy
$mailer->send('order@email.com', 'Order processed');
$logger->log('Order processed');
}
}
// ✅ DOBŘE - Dependency Injection
class OrderServiceWithDI
{
private Mailer $mailer;
private Logger $logger;
public function __construct(Mailer $mailer, Logger $logger)
{
$this->mailer = $mailer;
$this->logger = $logger;
}
public function process()
{
$this->mailer->send('order@email.com', 'Order processed');
$this->logger->log('Order processed');
}
}
// Použití - lze snadno měnit implementace, testovat s mocky
$service = new OrderServiceWithDI(
new SmtpMailer(), // nebo MockMailer() v testech
new FileLogger() // nebo DatabaseLogger()
);
// ============================================================================
// 9. 🔥 SINGLE RESPONSIBILITY - Jedna třída = jeden důvod ke změně
// ============================================================================
// ❌ ŠPATNĚ - Dělá všechno
class OrderGod
{
public function createOrder($data) { /* ... */ }
public function sendEmail($to, $subject) { /* ... */ }
public function logToDatabase($message) { /* ... */ }
public function validateCreditCard($number) { /* ... */ }
public function generatePdf($order) { /* ... */ }
}
// ✅ DOBŘE - Každá třída má jednu odpovědnost
class OrderCreator
{
public function create($data) { /* ... */ }
}
class EmailSender
{
public function send($to, $subject, $body) { /* ... */ }
}
class Logger
{
public function log($message) { /* ... */ }
}
class CreditCardValidator
{
public function validate($number): bool { /* ... */ }
}
class PdfGenerator
{
public function generate($order): string { /* ... */ }
}
// ============================================================================
// 10. 🎭 INTERFACE SEGREGATION - Malé, specifické interfaces
// ============================================================================
// ❌ ŠPATNĚ - Obrovský interface, vynucuje nepotřebné metody
interface WorkerGod
{
public function work();
public function eat();
public function sleep();
public function getPaid();
}
class Robot implements WorkerGod
{
public function work() { /* OK */ }
public function eat() { /* Robot nejí! */ }
public function sleep() { /* Robot nespí! */ }
public function getPaid() { /* Robot nedostává výplatu! */ }
}
// ✅ DOBŘE - Malé, specifické interfaces
interface Workable
{
public function work();
}
interface Eatable
{
public function eat();
}
interface Sleepable
{
public function sleep();
}
interface Payable
{
public function getPaid();
}
class Human implements Workable, Eatable, Sleepable, Payable
{
public function work() { /* ... */ }
public function eat() { /* ... */ }
public function sleep() { /* ... */ }
public function getPaid() { /* ... */ }
}
class RobotWorker implements Workable
{
public function work() { /* ... */ }
// Jen to, co skutečně umí!
}
// ============================================================================
// 11. 🏭 FACTORY PATTERN - Vytváření komplexních objektů
// ============================================================================
// ❌ ŠPATNĚ - Složité vytváření všude v kódu
$order = new Order();
$order->setCustomer($customer);
$order->setShippingAddress($address);
$order->setPaymentMethod($payment);
$order->setState(OrderState::NEW);
$order->setCreatedAt(new DateTime());
// ... 20 dalších setterů
// ✅ DOBŘE - Factory zapouzdřuje logiku vytváření
class OrderFactory
{
public function createFromCart(Cart $cart, Customer $customer): Order
{
$order = new Order();
$order->setCustomer($customer);
$order->setShippingAddress($customer->getDefaultAddress());
$order->setPaymentMethod($cart->getPaymentMethod());
$order->setState(OrderState::NEW);
$order->setCreatedAt(new DateTime());
$order->setTotal($cart->getTotal());
foreach ($cart->getItems() as $item) {
$order->addItem($item);
}
return $order;
}
}
// Použití:
$orderFactory = new OrderFactory();
$order = $orderFactory->createFromCart($cart, $customer);
// ============================================================================
// 12. 🔗 METHOD CHAINING (Fluent Interface)
// ============================================================================
// ❌ ŠPATNĚ - Hodně řádků
$query = new QueryBuilder();
$query->select('*');
$query->from('users');
$query->where('active', true);
$query->orderBy('created_at', 'DESC');
$query->limit(10);
$result = $query->get();
// ✅ DOBŘE - Fluent interface (vrací $this)
class QueryBuilder
{
private string $select = '';
private string $from = '';
private array $where = [];
public function select(string $columns): self
{
$this->select = $columns;
return $this; // Vrací sebe sama
}
public function from(string $table): self
{
$this->from = $table;
return $this;
}
public function where(string $column, $value): self
{
$this->where[] = [$column, $value];
return $this;
}
public function get(): array
{
// Execute query
return [];
}
}
// Použití - řetězení metod:
$result = (new QueryBuilder())
->select('*')
->from('users')
->where('active', true)
->orderBy('created_at', 'DESC')
->limit(10)
->get();
// ============================================================================
// 13. 💎 BONUS: Praktické tipy
// ============================================================================
// TIP 1: Používej ?? místo ternary pro null checks
$name = $user->name ?? 'Guest'; // místo: $user->name ? $user->name : 'Guest'
// TIP 2: Spaceship operator pro porovnání
usort($array, fn($a, $b) => $a <=> $b); // místo: $a > $b ? 1 : ($a < $b ? -1 : 0)
// TIP 3: Null safe operator (PHP 8.0+, ale dobrý vědět)
// $country = $user?->getAddress()?->getCountry()?->getName();
// TIP 4: Array destructuring
[$firstName, $lastName] = explode(' ', $fullName);
// TIP 5: Arrow functions (PHP 7.4+)
$prices = array_map(fn($item) => $item->price, $items);
// TIP 6: Typed properties (PHP 7.4+)
class Product
{
private int $id;
private string $name;
private float $price;
private ?string $description = null; // Nullable
}
// TIP 7: Spread operator
$array1 = [1, 2, 3];
$array2 = [...$array1, 4, 5, 6]; // [1, 2, 3, 4, 5, 6]
// TIP 8: Named arguments (PHP 8.0+, ale dobrý vědět)
// createOrder(customer: $customer, total: 1000, shipping: 'DHL');
// ============================================================================
// 📚 SHRNUTÍ - Co používat VŽDY:
// ============================================================================
/*
1. ✅ Konstanty místo magic strings/numbers
2. ✅ Type hints všude (int, string, array, vlastní třídy)
3. ✅ Early return (guard clauses)
4. ✅ Dependency Injection
5. ✅ Single Responsibility (malé, zaměřené třídy)
6. ✅ Value Objects pro důležitá data (Money, Email, Percentage)
7. ✅ Immutabilita kde to dává smysl
8. ✅ Null Object pattern místo null checks
9. ✅ PHPDoc kde type hints nestačí
10. ✅ Smysluplné názvy (ne $a, $tmp, $data)
❌ NIKDY:
- Magic strings/numbers bez konstant
- Velké třídy (God Objects)
- new v business logice (použij DI)
- Public properties (použij gettery/settery)
- Hluboké vnořené ify (pyramid of doom)
*/
Máš to! Celé najednou pro Gist. Chceš ještě něco konkrétního? 🚀