Özet
Veri aktarıcısı (DTO) kullanmak, geliştiriciler için yazımı keyifli olabilir. Ancak, bu süreç genellikle her çağrıda yansıma ve dağıtım maliyetleri ile sonuçlanır. Simple Data Objects, veri sınıfı başına özel bir kapanış oluşturur; bu sayede düz özellikler, çevrimiçi dizi okumalarına dönüşür. Üretilen kod, bir opcache ile önbelleğe alır ve PHP 8.4 ile gelen tembel nesneler, ilk özellik erişimine kadar sıvazlamayı erteleyerek performansı artırır.
PHP Uygulamalarında DTO Kullanımı
Modern PHP kod tabanları genellikle, ham giriş (bir istek, bir DB satırı, bir API yükü) ile iş mantığı arasında yer alan türlenmiş, değişmez sınıflar içeren bir veri nesne katmanı geliştirir. Nitelik yönlendirmeli DTO kütüphaneleri, bu işlemi yapmak oldukça keyifli hale getirdi:
class CreateOrderData extends BaseData
{
public function __construct(
#[Rules(['required', 'string', 'max:200'])]
public readonly string $title,
#[Cast(new DateTimeCast('Y-m-d'))]
public readonly \DateTime $deliveryDate,
public readonly ?string $notes = null,
) {}
}
$order = CreateOrderData::from($request->all());Ancak kolay yazım, genellikle yüksek işletme maliyetleri ile sonuçlanır. Tipik uygulamalar, her nesnenin her özelliği için genel bir sıvazlama hattımını geçmek üzere, yansımayı kurucuda gerçekleştirir ve her durum için alanları okur. Bu durum tek bir DTO için iyi çalışsa da, birkaç bin satırı sıvazlamanız gerektiğinde, performans sorunları ortaya çıkar.
Önerilen Yaklaşımlar
1. Yansımayı Sadece Bir Defa Yapın
İlk olarak, yansımayı önceden yapmak mantıklıdır. Bir sınıf için ilk from() çağrısında, paket bir ClassMeta oluşturur; bu, kurucunun bir tanımlamasıdır. Sonraki çağrılar, bu metadata’yı yeniden kullanır.
2. Döngüyü Derleyin
Metadata, her sınıf için statik olduğundan, her çağrıda yorumlamak yerine, paket her sınıf için özel bir kapanış üretir.
static function (array $d) use ($p, $pipes): \App\Data\CreateOrderData {
return new \App\Data\CreateOrderData(
\array_key_exists(, )
? []
: throw DataHydrationException::missingField(CreateOrderData::class, ),
\array_key_exists(, )
? ValueCaster::cast([1], [])
: throw DataHydrationException::missingField(CreateOrderData::class, ),
[] ?? null,
);
}Bu aşamadan elde edilen sonuçlar, analiz edilen DTO şekillerinde, yaklaşık 2.6 kat daha hızlı sıvazlama ve 2.2 kat daha hızlı seri hale getirme sağlar.
3. Başlangıçta Derlemeyi Yapmayın
Derlenmiş kapanışlar, ilk çağrıda yansımaları ve kod oluşturmayı gerektirir. PHP-FPM ile, bu her işçi için her dağıtımda olur. Metadata önbelleği, derlenmiş kodu kalıcı hale getirir.
MetadataRegistry::setStoragePath(storage_path());4. Okunmayanı Sıvazlamayın
PHP 8.4 ile gelen tembel nesneler (lazy objects), DTO’lar için mükemmel bir uyum sağlar. fromLazy() ilk özellik erişiminde çalışmaya başlar. Bu, eğer yalnızca %10 oranında okunan nesneler yaratıyorsanız performansı ciddi şekilde artırır.
$orders = array_map(OrderData::fromLazy(...), $rows);Ölçümler
En popüler veri nesne kütüphanesi ile karşılaştırıldığında, benzer DTO şekilleri için yapılan ölçümler şunlardır:
| Senaryo | Simple Data Objects | Popüler Alternatif | Avantaj |
|---|---|---|---|
| Sıvazlama — düz DTO | ~4,500,000 ops/s | ~130,000 ops/s | ~35× |
| Sıvazlama — iç içe DTO | ~2,200,000 ops/s | ~74,000 ops/s | ~30× |
| Sıvazlama — 20’lik koleksiyon | ~270,000 ops/s | ~7,500 ops/s | ~36× |
| Serileştirme — düz DTO | ~7,400,000 ops/s | ~200,000 ops/s | ~37× |
| Serileştirme — iç içe DTO | ~4,000,000 ops/s | ~117,000 ops/s | ~34× |
| 50k satırda zirve bellek kullanımı | 0.26 MB (lazyCollection()) | ~13 MB | ~50× daha az |
Sonuç
Hız, belirli kısıtlamalardan gelir. Bu çözüm yalnızca PHP 8.4 için geçerlidir ve tüm kurulum, tanımlayıcılar ve varsayılan özellikler üzerinden geçmektedir.
Deneyin
composer require std-out/simple-data-objects
Kaynak: Orijinal Makale


