CodeBlanks Logo Icon CodeBlanks

Meydan okuma - Tam PHP uygulaması

PHP kursu · Uzman · Ders 54/54

readonly, enum, match, Fiber ve Docker'la modern bir görev yönetim sistemi yazarsın.

Modern PHP: readonly + enum + match + Fiber

Son derste tüm katmanları modern PHP özellikleriyle taçlandırırsın. enum TaskStatus: string görev durumlarını sabit değer kümesiyle tanımlar. readonly class Task yapım sonrası değiştirilemez bir görev modeli verir; ilk atamadan sonra özellikler sabitlenir.

match ifadesi her duruma karşılık etiket üretir; switch'ten daha kısa ve katı tür dönüşümlü. Fiber ise kooperatif zamanlama sunar: bir yordam Fiber::suspend() ile bekletilebilir ve resume() ile devam edebilir. php_fpm ile Docker'da üretime hazır hale gelir.

Fiber ile kooperatif zamanlama

Fiber PHP 8.1'in kooperatif yordam yapısıdır. new Fiber($fn) bir yordam sarmalar; start() ile başlar, Fiber::suspend() ile bekletilir, resume() ile devam eder. isSuspended() durumu sorgular.

FiberScheduler bunları bir diziye toplar; run() her fiberi başlatır ve isSuspended() oldukça resume() çağırır. Böylece tek thread içinde iş parçacıkları gibi sıralı çalışma sağlanır. php_fpm ve Docker ile dağıtıma hazır bir uygulama ortaya çıkar; readonly + enum + match + Fiber birleşimi mini framework'ün çekirdeğini oluşturur.

Uygulama Modern PHP çekirdeğini yaz

Başlangıç kodunda ETL, ReflectionSerializer, Cache, benchmark, ReportService, RateLimiter, CSP, SessionGuard, needsRehash ve validateEmail hazır. Şimdi modern PHP çekirdeğini ekle.

enum TaskStatus: string ile Pending = "pending", InProgress = "in_progress", Done = "done" durumlarını tanımla. readonly class Task içinde id, title ve status özellikleri yapılandırma ile public olsun; varsayılan TaskStatus::Pending olsun. statusLabel(TaskStatus $s): string match ile her duruma karşılık Türkçe etiket döndürsün. FiberScheduler sınıfı add(callable) ile new Fiber eklesin, run() her fiberi start() etsin ve isSuspended() oldukça resume() çağırsın. Demo bir görev yazdırsın, iki fiber ekleyip çalıştırsın.

Örnek çözümü göster
string ile Pending, InProgress, Done durumlarını tanımla. readonly class Task içinde id, title, status özellikleri yapılandırma ile public olsun. statusLabel(TaskStatus $s): string match() ile her duruma karşılık Türkçe etiket döner. FiberScheduler add(callable) ile new Fiber ekler, run() her fiberi start() eder ve isSuspended() oldukça resume() çağırır.
Örnek çözüm:
<?php
// ETL + Reflection + Performans + Güvenlik + Modern: enum + readonly + match + Fiber yaz

function readRows(array $rows): Generator {
    foreach ($rows as $row) {
        yield $row;
    }
}

function transform(iterable $rows, callable $fn): Generator {
    foreach ($rows as $row) {
        yield $fn($row);
    }
}

function pipeline(array $rows, callable $fn): Generator {
    yield from transform(readRows($rows), $fn);
}

function loadAll(iterable $rows): array {
    $out = [];
    foreach ($rows as $row) {
        $out[] = $row;
    }
    return $out;
}

#[Attribute]
class Field {
    public function __construct(public string $name = "") {}
}

class Product {
    public function __construct(
        #[Field("id")] public int $id,
        #[Field("name")] public string $name,
    ) {}
}

class ReflectionSerializer {
    public function serialize(object $obj): array {
        $rc = new ReflectionClass($obj);
        $out = [];
        foreach ($rc->getProperties() as $prop) {
            $attrs = $prop->getAttributes(Field::class);
            if (empty($attrs)) continue;
            $field = $attrs[0]->newInstance();
            $key = $field->name !== "" ? $field->name : $prop->getName();
            $out[$key] = $prop->getValue($obj);
        }
        return $out;
    }
}

class Cache {
    private array $store = [];
    public function get(string $key, callable $loader) {
        if (!isset($this->store[$key])) {
            $this->store[$key] = $loader();
        }
        return $this->store[$key];
    }
}

function benchmark(callable $fn): array {
    $start = hrtime(true);
    $result = $fn();
    $elapsed = (int)((hrtime(true) - $start) / 1000000);
    $mem = memory_get_usage(true);
    return ["result" => $result, "ms" => $elapsed, "bytes" => $mem];
}

class ReportService {
    private PDO $pdo;
    public function __construct(PDO $pdo) {
        $this->pdo = $pdo;
        $this->pdo->exec("CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, category_id INTEGER)");
        $this->pdo->exec("CREATE TABLE categories (id INTEGER PRIMARY KEY, name TEXT)");
        $this->pdo->prepare("INSERT INTO categories (name) VALUES (?)")->execute(["kirtasiye"]);
        $this->pdo->prepare("INSERT INTO products (name, category_id) VALUES (?, ?)")->execute(["kale", 1]);
        $this->pdo->prepare("INSERT INTO products (name, category_id) VALUES (?, ?)")->execute(["defter", 1]);
    }
    public function eagerReport(): array {
        $sql = "SELECT p.name AS product, c.name AS category FROM products p JOIN categories c ON p.category_id = c.id ORDER BY p.id";
        return $this->pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
    }
}

class RateLimiter {
    private array $counts = [];
    public function __construct(private int $limit = 3) {}
    public function allow(string $key): bool {
        $this->counts[$key] = ($this->counts[$key] ?? 0) + 1;
        return $this->counts[$key] <= $this->limit;
    }
}

function applyCsp(): void {
    header("Content-Security-Policy: default-src 'self'");
}

class SessionGuard {
    public function regenerate(): void {
        session_regenerate_id(true);
    }
}

function needsRehash(string $hash): bool {
    return password_needs_rehash($hash, PASSWORD_DEFAULT);
}

function validateEmail(string $email): bool {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

enum TaskStatus: string {
    case Pending = "pending";
    case InProgress = "in_progress";
    case Done = "done";
}

readonly class Task {
    public function __construct(
        public int $id,
        public string $title,
        public TaskStatus $status = TaskStatus::Pending,
    ) {}
}

function statusLabel(TaskStatus $s): string {
    return match($s) {
        TaskStatus::Pending => "BEKLEMEDE",
        TaskStatus::InProgress => "DEVAM",
        TaskStatus::Done => "TAMAM",
    };
}

class FiberScheduler {
    private array $fibers = [];
    public function add(callable $fn): void {
        $this->fibers[] = new Fiber($fn);
    }
    public function run(): void {
        foreach ($this->fibers as $f) {
            $f->start();
            while ($f->isSuspended()) {
                $f->resume();
            }
        }
    }
}

// Demo:
$task = new Task(1, "özeti yaz", TaskStatus::InProgress);
echo "görev: " . $task->title . "\n";
echo "durum: " . statusLabel($task->status) . "\n";

$sched = new FiberScheduler();
$sched->add(function() {
    echo "fiber1: başladı\n";
    Fiber::suspend();
    echo "fiber1: devam\n";
});
$sched->add(function() {
    echo "fiber2: başladı\n";
});
$sched->run();
echo "php_fpm: hazır\n";

Kendini test et

  1. Modern PHP kavramlarını eşleştir

    enum
    Sabit değer kümesi
    readonly
    İlk atamadan sonra sabit
    match
    Desen karşılaştırma
    Fiber
    Kooperatif yordam
  2. readonly özellik ne sağlar?

    • Özelliğin serializable olmasını
    • İlk atamadan sonra değiştirilememesini
    • Özelliğin otomatik doldurulmasını
    • Özelliğin statik olmasını
    Cevabı göster

    İlk atamadan sonra değiştirilememesini — Doğru. readonly özellik ilk atamadan sonra değiştirilemez; sabit değer nesneleri için idealdir.

  3. match ifadesini tamamla

    return ____($s) {
        TaskStatus::Pending => "BEKLEMEDE",
        TaskStatus::InProgress => "DEVAM",
        TaskStatus::Done => "TAMAM",
    };
    Cevabı göster
    return match($s) {
        TaskStatus::Pending => "BEKLEMEDE",
        TaskStatus::InProgress => "DEVAM",
        TaskStatus::Done => "TAMAM",
    };

Uzman bölümündeki tüm dersler