İçeriğe geç
Muhammet Şafak
Diller 3 dk okuma

PHP 8.0 ile kod yazmak: attribute'lar ve nullsafe operatör

PHP 8.0 çıktı. Attribute'ları ve nullsafe operatörü gerçek kodda nasıl kullandığımı, ne kazandırdığını paylaşıyorum.


PHP 8.0 geçen ay çıktı. Kasım yazısında union types, match ve named arguments üzerine hazırlık notları almıştım. Bu ay gerçek projede PHP 8.0 çalıştırıyorum ve iki özellik beklediğimden daha fazla günlük koduma girdi: attribute’lar ve nullsafe operatörü.

Attribute’lar

PHP’de metadata eklemek için yıllarca PHPDoc yorumları kullandık. @Route, @Column, @ORM\Entity gibi. Bu yorumlar derleyici tarafından dikkate alınmıyor; runtime’da reflection ile ayrıştırılıyor. Sözdizimi standardize edilmemiş, IDE desteği değişken, hata yakalama geç.

PHP 8.0’daki Attribute özelliği bunu dil düzeyine taşıyor.

Önce bir attribute sınıfı tanımlıyorsunuz:

#[Attribute]
class Route
{
    public function __construct(
        public string $path,
        public string $method = 'GET',
    ) {}
}

Sonra bu attribute’u bir sınıfa ya da metoda uyguluyorsunuz:

class UserController
{
    #[Route('/users', method: 'GET')]
    public function index(): JsonResponse
    {
        // ...
    }

    #[Route('/users', method: 'POST')]
    public function store(Request $request): JsonResponse
    {
        // ...
    }
}

Reflection ile okumak:

$reflection = new ReflectionMethod(UserController::class, 'index');
$attributes = $reflection->getAttributes(Route::class);

foreach ($attributes as $attr) {
    $route = $attr->newInstance();
    echo $route->path;   // /users
    echo $route->method; // GET
}

Framework katmanı bu mekanizmayı kullanarak rotaları, doğrulama kurallarını, serialization davranışını sınıf tanımından okuyabiliyor. PHPDoc’un yaptığı ama dil garantisi olmadan yaptığı işi artık dil kendisi yapıyor.

PHPDoc yorumlarına kıyasla attribute’ların en somut avantajı tip güvenliği. @Route('/users') yazdığınızda PHP bunu yorum olarak görüyor; yanlış yazıp yazmadığınızı yakalamıyor. #[Route('/users')] yazdığınızda ise Route bir PHP sınıfı — IDE otomatik tamamlama sunuyor, yanlış parametre yazdığınızda hata veriyor.

Laravel henüz bunu birincil API olarak kullanmıyor; ama ekosistemde bu yönde hareket var. Kendi küçük framework bileşenlerimde ve CLI araçlarımda attribute’ları kullanmaya başladım.

Nullsafe Operatörü (?->)

Bu özellik basit ama kodu ciddi temizliyor. Zincirli nesne erişimlerinde null kontrolü:

// PHP 7 — zincirleme null kontrolleri
$country = null;
if ($user !== null) {
    $address = $user->getAddress();
    if ($address !== null) {
        $country = $address->getCountry();
    }
}

// ya da daha kısa ama hâlâ dağınık
$country = $user ? ($user->getAddress() ? $user->getAddress()->getCountry() : null) : null;

PHP 8.0 ile:

$country = $user?->getAddress()?->getCountry();

Zincirin herhangi bir noktasında null dönerse tüm ifade null oluyor. Exception fırlamıyor, sessizce sonlanıyor.

Gerçek bir senaryo: bir kullanıcının aktif aboneliğinin bitiş tarihini almak.

// PHP 7
$expiry = null;
$sub = $user->activeSubscription();
if ($sub) {
    $plan = $sub->plan();
    if ($plan) {
        $expiry = $plan->expiresAt();
    }
}

// PHP 8.0
$expiry = $user->activeSubscription()?->plan()?->expiresAt();

Satır sayısı düşüyor, niyet net kalıyor.

Bir uyarı: nullsafe operatör sessizce null döndürüyor. Bu bazen istenen davranış, bazen değil. Eğer $user->getAddress() asla null dönmemesi gereken bir durumsa, ?-> kullanmak bir hatayı gizliyor olabilir. Nullsafe’i yalnızca gerçekten optional zincir adımlarında kullanmak gerekiyor; her yeri ?-> ile kaplamak null’ların sessizce yayılmasına yol açabilir.

Named Arguments ile constructor promotion birlikteliği

PHP 8.0’da gelen constructor property promotion da iki haftadır yoğun kullandığım bir özellik:

// PHP 7 — tekrar eden yazım
class CreatePostRequest
{
    public string $title;
    public string $body;
    public ?int   $categoryId;

    public function __construct(string $title, string $body, ?int $categoryId = null)
    {
        $this->title      = $title;
        $this->body       = $body;
        $this->categoryId = $categoryId;
    }
}

// PHP 8.0 — constructor promotion
class CreatePostRequest
{
    public function __construct(
        public string $title,
        public string $body,
        public ?int   $categoryId = null,
    ) {}
}

Named arguments ile birlikte:

$request = new CreatePostRequest(
    title:      'PHP 8.0 ile Kod Yazmak',
    body:       $content,
    categoryId: 3,
);

Uzun parametre listelerinde bu okunabilirliği doğrudan artırıyor.

Constructor promotion özellikle DTO (Data Transfer Object) ve value object sınıflarında çok işe yarıyor. Daha önce beş satır olan yapıcı metod tek bir satıra iniyor. Tek dezavantajı: yapıcı metodun başka bir mantık da içermesi gerekiyorsa karışıklık çıkıyor. Salt veri taşıyan sınıflarda birebir.

JIT hakkında bir not

PHP 8.0’ın büyük teknik haberi JIT derleyici. Web uygulamalarında performans artışı ölçülebilir ama dramatik değil; JIT’in asıl kazancı CPU yoğun işlemlerde. Tipik bir Laravel API’sinde bottleneck veritabanı sorgularında, JIT bunları etkilemiyor. Abartılı beklentiye girmemek gerekiyor.


PHP 8.0 ile bir ay geçirdikten sonra görüşüm şu: attribute’lar ve nullsafe operatörü hemen değer yaratıyor. Constructor promotion boilerplate’i gerçekten azaltıyor. match, union types ve named arguments daha dikkatli uygulanması gereken özellikler — doğru yerde güçlü, yanlış yerde gereksiz karmaşıklık. Sürümü production’a almak için acele etmiyorum; ama yeni projelerde PHP 8.0’dan başlamayı planlıyorum.

Etiketler: #PHP
Paylaş:

İlgili Yazılar

Sitede Ara

Yazı, proje ve sayfalarda arama yapmak için yazmaya başlayın.

Esc ile kapat Pagefind ile güçlendirildi