Jump to content
  • entries
    2
  • comments
    0
  • views
    81

PHP 8+ ile Encapsulation ve Private Properties


PHP 8+ ile birlikte gelen modern özelliklerle encapsulation (kapsülleme) prensibini derinlemesine inceleyelim.

Temel Kavram: Neden Private?

Verileri gizleyip davranışı ortaya çıkarmanın amacı:

  • Değişimin kontrolü: Dışarıdan doğrudan erişimi engelleyerek değişiklikleri kontrol edersiniz
  • İç implementasyon özgürlüğü: İç yapıyı değiştirdiğinizde dış kodlar etkilenmez
  • Veri tutarlılığı: Validation ve business logic'i merkezi bir noktada yönetirsiniz

PHP 8.0+ Constructor Property Promotion

PHP 8.0'ın getirdiği en önemli özelliklerden biri:

<?php

//  Eski yöntem (PHP 7.x)
class User
{
    private string $name;
    private string $email;
    private int $age;
    
    public function __construct(string $name, string $email, int $age)
    {
        $this->name = $name;
        $this->email = $email;
        $this->age = $age;
    }
}

//  Yeni yöntem (PHP 8.0+)
class User
{
    public function __construct(
        private string $name,
        private string $email,
        private int $age
    ) {}
    
    // Getter'lar ile davranış ortaya çıkarma
    public function getName(): string
    {
        return $this->name;
    }
    
    public function getEmail(): string
    {
        return $this->email;
    }
    
    public function getAge(): int
    {
        return $this->age;
    }
    
    // Kontrollü değişim
    public function updateEmail(string $newEmail): void
    {
        if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException('Geçersiz email formatı');
        }
        $this->email = $newEmail;
    }
    
    public function celebrateBirthday(): void
    {
        $this->age++;
    }
}

PHP 8.1+ Readonly Properties

PHP 8.1 ile gelen readonly özelliği immutability sağlar:

<?php

class Product
{
    public function __construct(
        private readonly string $id,
        private readonly string $name,
        private float $price,
        private readonly DateTime $createdAt
    ) {}
    
    // ID ve name değiştirilemez (readonly)
    public function getId(): string
    {
        return $this->id;
    }
    
    public function getName(): string
    {
        return $this->name;
    }
    
    // Fiyat değiştirilebilir ama kontrollü
    public function getPrice(): float
    {
        return $this->price;
    }
    
    public function applyDiscount(float $percentage): void
    {
        if ($percentage < 0 || $percentage > 100) {
            throw new InvalidArgumentException('İndirim 0-100 arasında olmalı');
        }
        
        $this->price = $this->price * (1 - $percentage / 100);
    }
    
    public function getCreatedAt(): DateTime
    {
        // ⚠️ DateTime mutable bir object, clone ile koruma
        return clone $this->createdAt;
    }
}

$product = new Product('P001', 'Laptop', 15000.0, new DateTime());
echo $product->getPrice(); // 15000

$product->applyDiscount(20); // %20 indirim
echo $product->getPrice(); // 12000

// $product->id = 'P002'; //  HATA: Readonly property

PHP 8.2+ Readonly Classes

Tüm class'ı immutable yapmak için:

<?php

readonly class Money
{
    public function __construct(
        public float $amount,
        public string $currency
    ) {}
    
    // Yeni bir Money objesi döndürerek değişimi yönetme
    public function add(Money $other): self
    {
        if ($this->currency !== $other->currency) {
            throw new InvalidArgumentException('Para birimleri eşleşmiyor');
        }
        
        return new self(
            $this->amount + $other->amount,
            $this->currency
        );
    }
    
    public function multiply(float $factor): self
    {
        return new self(
            $this->amount * $factor,
            $this->currency
        );
    }
    
    public function format(): string
    {
        return number_format($this->amount, 2) . ' ' . $this->currency;
    }
}

$price = new Money(100, 'TRY');
$tax = new Money(18, 'TRY');

$total = $price->add($tax);
echo $total->format(); // 118.00 TRY

// $price->amount = 200; //  HATA: Readonly property

Pratik Örnek: E-Ticaret Sepet Sistemi

<?php

readonly class CartItem
{
    public function __construct(
        public string $productId,
        public string $productName,
        public float $unitPrice,
        public int $quantity
    ) {
        if ($quantity <= 0) {
            throw new InvalidArgumentException('Miktar pozitif olmalı');
        }
    }
    
    public function getTotalPrice(): float
    {
        return $this->unitPrice * $this->quantity;
    }
    
    public function changeQuantity(int $newQuantity): self
    {
        return new self(
            $this->productId,
            $this->productName,
            $this->unitPrice,
            $newQuantity
        );
    }
}

class ShoppingCart
{
    /** @var array<string, CartItem> */
    private array $items = [];
    
    public function addItem(CartItem $item): void
    {
        $productId = $item->productId;
        
        if (isset($this->items[$productId])) {
            $existingItem = $this->items[$productId];
            $newQuantity = $existingItem->quantity + $item->quantity;
            $this->items[$productId] = $existingItem->changeQuantity($newQuantity);
        } else {
            $this->items[$productId] = $item;
        }
    }
    
    public function removeItem(string $productId): void
    {
        unset($this->items[$productId]);
    }
    
    public function updateQuantity(string $productId, int $quantity): void
    {
        if (!isset($this->items[$productId])) {
            throw new InvalidArgumentException('Ürün sepette bulunamadı');
        }
        
        if ($quantity <= 0) {
            $this->removeItem($productId);
        } else {
            $this->items[$productId] = $this->items[$productId]
                ->changeQuantity($quantity);
        }
    }
    
    /** @return array<CartItem> */
    public function getItems(): array
    {
        return array_values($this->items);
    }
    
    public function getTotalAmount(): float
    {
        return array_reduce(
            $this->items,
            fn($total, $item) => $total + $item->getTotalPrice(),
            0.0
        );
    }
    
    public function getItemCount(): int
    {
        return array_reduce(
            $this->items,
            fn($total, $item) => $total + $item->quantity,
            0
        );
    }
    
    public function clear(): void
    {
        $this->items = [];
    }
}

// Kullanım
$cart = new ShoppingCart();

$laptop = new CartItem('P001', 'Laptop', 15000, 1);
$mouse = new CartItem('P002', 'Mouse', 250, 2);

$cart->addItem($laptop);
$cart->addItem($mouse);

echo "Toplam ürün: " . $cart->getItemCount() . "\n"; // 3
echo "Toplam tutar: " . $cart->getTotalAmount() . " TRY\n"; // 15500 TRY

$cart->updateQuantity('P002', 5);
echo "Güncel tutar: " . $cart->getTotalAmount() . " TRY\n"; // 16250 TRY

//  Dışarıdan doğrudan erişim yok
// $cart->items['P001']->quantity = 10; // Private property

 

Özet

PHP 8+ ile encapsulation'ı uygulamak için:

  1. Constructor Property Promotion kullanın (PHP 8.0+)
  2. Readonly properties ile immutability sağlayın (PHP 8.1+)
  3. Readonly classes ile tam immutable objeler oluşturun (PHP 8.2+)
  4. Getter/Setter yerine davranışı ortaya çıkaran metodlar yazın
  5. Validation logic'i her zaman içeride tutun
  6. Return type declarations ile tip güvenliği sağlayın

Bu yaklaşım sayesinde kodunuz daha güvenli, test edilebilir ve sürdürülebilir olur!

PHP 8+ ile "tüm özellikleri private yapmak" şu anlama gelir:

  1. Varsayılan: Veriyi private yap, davranışı public metotla aç.

  2. Daha Kolay: private özellikleri Constructor Property Promotion ile hızlıca tanımla.

  3. Daha Güvenli (Durum): string veya int gibi ilkel tipler yerine private Enum kullanarak iş kurallarını tipe dönüştür.

  4. En Güvenli (Değişmezlik): Asla değişmemesi gereken veriler için private readonly kullanarak nesnenin temelini kilitle.

0 Comments


Recommended Comments

There are no comments to display.

×
×
  • Create New...

Important Information