# Attribute tabanlı bir mapping katmanında reflection maliyetini production'da nasıl kontrol altında tutarım?

> Attribute'ları istek başına değil sınıf başına bir kez okuyup mapping planını cacheleyin; profil hâlâ reflection gösteriyorsa üretilmiş PHP'ye derleyin.

- Soruldu: 2026-10-19
- Yanıtlandı: 2026-10-23
- Soran: Arda
- Etiketler: php, performans
- Kaynak: https://muhammetsafak.com/tr/sor-bakalim/attribute-mapping-katmaninda-reflection-maliyetini-kontrol-altinda-tutarim/
- Dil: tr-TR
- Yazar: Muhammet Şafak

---
**Soru:** PHP attribute'larını okuyup DTO'lara/array'lere dönüştüren kendi serializer'ımı yazdım. Sorun şu ki, her istekte ilgili sınıf için `ReflectionClass` açıp attribute'ları yeniden okuyorum.

Ölçek büyüdükçe (yüksek RPS, derin nesne grafikleri) bu per-request reflection maliyetinden endişeleniyorum. FPM altında her istek soğuk başladığı için bu iş her seferinde tekrarlanıyor gibi. Production'da bu maliyeti nasıl kontrol altında tutarım?


Kısa cevap: Reflection'ın kendisi düşmanınız değil — onu her istekte çalıştırmak düşmanınız.

## Kısa cevap

[Attribute'ları bir kez okuyun](/tr/blog/php-8-0-ile-kod-yazmak-attribute-lar-ve-nullsafe-operator/), mapping'i cachelenebilir bir "plan"a (ideal olarak üretilmiş PHP koduna) derleyin; o zaman reflection maliyeti sıcak yoldan pratikte silinir.

## Neden

1. **Worker cache'i mi, runtime cache'i mi?** Uzun ömürlü bir worker'da (Octane/RoadRunner/FrankenPHP) statik array cache istekler arası yaşar — reflection worker ömrü başına bir kez çalışır. Klasik FPM'de her istek soğuktur; orada APCu ya da derlenmiş bir cache dosyası şart.
2. **Reflection ile OPcache ilişkisine dikkat.** OPcache reflection sonuçlarını cachelemez; yalnızca derlenmiş kodu cacheler. Yani kazanç, işi reflection'dan alıp üretilmiş/derlenmiş koda ya da kalıcı bir cache'e taşımaktan gelir.

## Ne yapmalı

1. **Önce ölçün, sonra optimize edin.** Blackfire/XHProf ile profil alın. İstek başına birkaç sınıf üzerinde reflection çoğu zaman mikrosaniyeler mertebesindedir; profilde görünmüyorsa fazla mühendislik yapmayın. Ama yüksek RPS ve derin grafiklerde toplam hızla birikir.
2. **Ayrıştırılmış metadata'yı cacheleyin.** Pahalı kısım mapping'in kendisi değil, `ReflectionClass` + attribute okumadır. Her sınıf için bir kez ayrıştırın; ortaya çıkan düz "mapping plan"ını sınıf adına göre bir statik cache'te (APCu/PSR-16) tutun.
   ```php
   final class MappingPlanCache
   {
       /** @var array<class-string, MappingPlan> */
       private static array $plans = [];

       public function planFor(string $class): MappingPlan
       {
           // İstek/worker içinde sınıf başına yalnızca bir kez derlenir.
           return self::$plans[$class] ??= $this->compile($class);
       }

       private function compile(string $class): MappingPlan
       {
           $ref = new \ReflectionClass($class);
           // attribute'lar burada bir kez okunur, düz bir plana çevrilir
           return MappingPlan::fromReflection($ref);
       }
   }
   ```
3. **En hızlı yol için üretilmiş PHP'ye derleyin.** Symfony Serializer'ın varsayılan `ObjectNormalizer`'ı ve Doctrine'in hydration adımı da hâlâ reflection üzerinden çalışır; reflection'sız, doğrudan property erişimiyle mapping yapan bir PHP sınıfı üretmek istiyorsanız bunu kendi derleme adımınızla siz kurmalısınız. OPcache bunu bytecode olarak servis eder. "Production modu" tam olarak budur.
4. **Cache invalidation'ı deploy'a bağlayın.** Derlenmiş cache'i bir deploy hash'iyle anahtarlayın ya da deploy'da temizleyin. İstek başına invalidate etmeyin (amacı boşa çıkar); ama şema değişince de bayat bırakmayın.

**Sonuç:** Ben olsam işe per-worker statik cache ile başlardım (tek satır: `??=`), ölçer ve ancak profil hâlâ reflection'ı sıcak yolda gösteriyorsa üretilmiş-PHP derlemesine geçerdim. Octane altında tek başına statik cache sorunu çoğu zaman yok eder; FPM altında üstüne APCu koyarsınız. İhtiyacınız olmayan bir derleyiciyi elle yazmayın — önce ölçün, katmanı ölçüm zorlayınca ekleyin.

## İlgili Yazılar

- [PHP 8.0 ile kod yazmak: attribute'lar ve nullsafe operatör](/tr/blog/php-8-0-ile-kod-yazmak-attribute-lar-ve-nullsafe-operator/) — Blog
- [Container'larım sürekli OOMKilled oluyor, memory limit'lerini PHP-FPM ve Go için nasıl doğru ayarlarım?](https://muhammetsafak.com/tr/sor-bakalim/containerlarim-oomkilled-memory-limitlerini-php-fpm-go-ayarlarim/) — Sor Bakalım
- [Eşzamanlı HTTP çağrıları için ham Fiber mı, ReactPHP/AMPHP mı, yoksa Swoole mu seçmeliyim?](https://muhammetsafak.com/tr/sor-bakalim/eszamanli-http-cagrilari-ham-fiber-reactphp-amphp-swoole/) — Sor Bakalım
- [Heap erişimini önlemek ve index-only scan almak için covering index'e (INCLUDE) ne zaman geçmeliyim?](https://muhammetsafak.com/tr/sor-bakalim/index-only-scan-covering-indexe-ne-zaman-gecmeliyim/) — Sor Bakalım
