136okunma
Proje
CPalius CMF

Proje Detayı

Merhabalar efenim, bir süredir aklımda olan ancak hiç bir zaman başlamaya yeltenemediğim aslında gerek duymadığım ama dünyada benim de bir dikili ağacım çakılı çivim olsun isteyerek bir projeye kolları sıvadım.

Projemin amacı aslında şu anda piyasadaki en bilinen en büyük CMF ve CMS sistemlerinin hepsini detaylıca inceleyip kapsamlıca artılarını eksilerini not ederek çok daha geniş kapsamlı ancak çok daha dayanıklı bir içerik yönetim framework'ü yapmaktı ve yavaştan başlayayım bakalım neler çıkar diyerekten kolları sıvadım.

Bu süreçte bolca yapay zeka desteği aldım hatta projemin whitepaper dosyasını da sağolsun gemini benim yerime yazdı ki buraya ekleyeceğim aşağıdan detaylarını inceleyebilirsiniz o kadar uzun yazmaya halim vaktim var aslında ama üşengeçliğim el vermiyor :)

Aradan geçen zamanda proje ilk günkü taslağından epey yol katetti, o yüzden bu yazıyı da güncel haliyle yeniden yazma ihtiyacı hissettim. İlk whitepaper'da "yapılacaklar" listesinde duran bazı maddeler (Safe Mode / Kurtarma Konsolu gibi) artık gerçek kod, üstüne de whitepaper'da hiç bahsi geçmeyen REST API Gateway, Hook Sistemi, Cron/Otomasyon Motoru ve Plugin katmanı gibi koca koca bölümler eklendi. Aşağıda projenin bugünkü, güncel halini bulacaksınız.

CPalius CMF: Bir İçerik Yönetim Sisteminden "Kurşun Geçirmez" Kurumsal Uygulama Framework'üne Evrim

  • Yayın Türü: Teknik İnceleme (Whitepaper) & Mimari Yol Haritası (RFC)
  • Sürüm: v1.1 (Güncel)
  • Hedef Kitle: Kıdemli PHP Geliştiricileri, Sistem Mimarları, Açık Kaynak Geliştiricileri ve Yapay Zeka Ajanları
  • Yazar / Kurucu: Ali Çömez (slaweally)
  • Teknoloji Yığını: PHP 8.2+, Symfony 7.4 LTS, Doctrine ORM, AssetMapper, Tailwind CSS Standalone Binary

Giriş: Teori, Pratik ve "Tekerleği Yeniden İcat Etme" Sancısı

Modern web ekosisteminde yeni bir projeye başlarken geliştiriciler kendilerini her zaman iki ucu keskin bir bıçağın üzerinde bulurlar. Bir yanda, hazır eklenti ve tema sistemleriyle donatılmış ancak hantal veritabanı şemaları, teknik borç (technical debt) dağları ve güvenlik zaafiyetleriyle boğuşan klasik CMS'ler (WordPress, Drupal) vardır. Diğer yanda ise her şeye sıfırdan başlamayı gerektiren, her projede auth, ACL, dosya yönetimi ve admin paneli gibi temel bileşenleri sıfırdan yazarak zaman kaybettiren saf modern framework'ler (Symfony, Laravel) bulunur.

Sıfırdan kendi MVC yapısını yazmak kulağa romantik gelse de günümüz standartlarında rasyonel değildir. Bir framework sadece yönlendirme (routing) ve kontrolcülerden (controllers) ibaret değildir; güvenlik (CSRF, XSS, SQLi engelleme), Dependency Injection (DI) Container yönetimi ve HTTP katmanı gibi devasa bir "görünmez buzdağı" barındırır.

İşte CPalius, bu iki dünya arasındaki köprüyü kurmak; geliştiriciyi tekerleği yeniden icat etme sancısından kurtarırken ona dünya standartlarında, optimize, güvenli ve genişletilebilir bir altyapı sunmak amacıyla doğdu. Bu döküman, CPalius'un en temiz skeleton kurulumundan, "çökmeyen" bir işletim sistemi mimarisine; ardından bir CMS'ten "Kurumsal Uygulama Framework'üne" (Enterprise Application Framework) uzanan teknik yolculuğunun en ince ayrıntısına kadar dökümüdür.

1. BÖLÜM: "Pristine Root" ve Klasör Mimarisi

Standart bir Symfony projesinde üçüncü parti paketler ve tarifler (recipes) yüklendikçe projenin ana dizini (root) dosya ve klasör çöplüğüne döner. Geliştiricinin kendi yazdığı kodlar ile framework'ün sistem dosyaları birbirine karışır. CPalius, projenin ilk gününde bu karmaşaya savaş açtı ve "Pristine Root" (Tertemiz Ana Dizin) politikasını benimsedi.

Ana dizinde sadece neyin nerede olduğunu gösteren 4 temel klasör, .env dosyası ve composer.json kalacak şekilde tüm mimariyi izole ettik.

Klasör Hiyerarşisi

CPalius/ (Ana Dizin)
│
├── cp-core/ # === KERNEL & SYSTEM SPACE ===
│ ├── bin/ # Konsol komut aracı (bin/console)
│ ├── config/ # Core konfigürasyonları (bundles, packages, routes)
│ ├── migrations/ # Çekirdek veritabanı göç dosyaları (Git'e tabi)
│ ├── src/ # Çekirdek PHP sınıfları (App\ namespace)
│ └── var/ # Cache, log ve SQLite dosyaları (Yazma alanı, gitignore)
│
├── cp-includes/ # === DEPENDENCIES SPACE ===
│ └── vendor/ # Composer paketleri (Symfony ve kütüphaneler)
│
├── cp-content/ # === USER & DEVELOPER SPACE ===
│ ├── config/ # Config Sync alanı (YAML olarak taşınan altyapı)
│ ├── modules/ # Bağımsız modüller/eklentiler (Modules\ namespace)
│ ├── themes/ # Kullanıcı arayüz temaları
│ └── translations/ # Global arayüz çevirileri (tr, en)
│
├── public/ # === WEB ROOT === (Dışarıya açık tek klasör)
│ ├── index.php # Front Controller (Tek giriş kapısı)
│ └── assets/ # Derlenmiş/Semlink edilmiş frontend varlıkları
│
├── .env # Ortam yapılandırmaları
└── composer.json # CPalius özel yol eşlemeleri

Symfony Flex'in İç Mekanizmalarını Bükmek

Bu yapıyı kurabilmek için Symfony Flex ve Composer'ın yapılandırma yeteneklerini en uç sınırlarına kadar zorladık. composer.json dosyasını Flex'e kendi klasör yapımızı dikte edecek şekilde yapılandırdık:

{ "config": { "vendor-dir": "cp-includes/vendor", "bin-dir": "cp-core/bin" }, "autoload": { "psr-4": { "App\\": "cp-core/src/", "Modules\\": "cp-content/modules/", "DoctrineMigrations\\": "cp-core/migrations/" } }, "extra": { "root-dir": ".", "bin-dir": "cp-core/bin", "config-dir": "cp-core/config", "src-dir": "cp-core/src", "var-dir": "cp-core/var", "public-dir": "public", "runtime": { "project_dir": ".", "dotenv_path": ".env" } }
}

Teknik Not: Sadece config.vendor-dir değiştirmek yetersizdir. Symfony Flex'in içindeki komutlar (cache:clear, assets:install), yolları hesaplarken composer'ın standart konfigürasyonlarından değil, extra bloğu altındaki parametrelerden beslenir. Bu parametreler eklenmediğinde sistem derleme aşamasında çöküyordu. Bu sayede tüm Flex tariflerinin doğrudan cp-core altına kurulmasını garanti altına aldık.

2. BÖLÜM: "Core Never Dies" (Çökmeyen Çekirdek) Mimarisi

Bir CMF geliştirirken en büyük kabus, kullanıcı alanından (cp-content/modules) gelebilecek kötü yazılmış veya bozuk kodların tüm sistemi kilitlemesidir. PHP'de gerçek bir işletim sistemi seviyesinde "sandbox" olmadığı için, CPalius artık dört kademeli aktif savunma ve karantina hattı geliştirmiştir — ilk taslakta üç kademeydi, dördüncüsü rota yükleme katmanına eklendi.

1. Savunma Hattı: Tavuk-Yumurta Probleminin Çözümü (bundles.php)

Symfony boot edilirken ilk olarak config/bundles.php dosyası okunur. Bu aşamada henüz veritabanı bağlantısı, servis konteyneri (container) veya autoloader tam olarak ayağa kalkmamıştır. Eğer modüllerin aktiflik durumunu doğrudan veritabanından okumaya çalışırsak, sistem daha boot edilemeden kilitlenir.

Çözüm: CPalius, bir modül aktif veya pasif edildiğinde cp-core/config/active_modules.php adında statik, PHP opcache dostu bir dizi (array) dosyası üretir. bundles.php sadece bu dosyayı okur:

// cp-core/config/bundles.php
$bundles = [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true], App\Core\CoreBundle::class => ['all' => true], // Çekirdek her zaman ayakta!
];
$activeModulesFile = __DIR__ . '/active_modules.php';
if (file_exists($activeModulesFile)) { $activeModules = require $activeModulesFile; foreach ($activeModules as $moduleClass) { if (class_exists($moduleClass)) { $bundles[$moduleClass] = ['all' => true]; } }
}
return $bundles;

2. Savunma Hattı: Çalışma Zamanı İzolasyonu (Kernel::boot Override)

Sınıfın fiziksel olarak var olması (class_exists), o modülün kodunun hatasız çalıştığı anlamına gelmez. Modülün kendi boot() metodu içinde fırlatacağı bir TypeError veya RuntimeException tüm sistemi çökertebilir.

Bunu engellemek için Kernel::boot() metodunu override ederek modül seviyesindeki tüm boot süreçlerini try/catch (\Throwable) zırhıyla kuşattık:

// cp-core/src/Kernel.php
public function boot(): void
{ parent::boot(); foreach ($this->getBundles() as $bundle) { if (str_starts_with($bundle->getNamespace(), 'Modules\\')) { try { // Modülleri izole olarak boot et $bundle->boot(); } catch (\Throwable $e) { // Çekirdeği koru, hatayı karantina günlüğüne yaz $this->quarantineModule($bundle, $e); } } }
}

3. Savunma Hattı: Compile-Time Kilitlenme Koruması & Dry-Run

Eklentideki bir services.yaml dosyasında geçersiz bir YAML sözdizimi varsa veya yanlış bir Dependency Injection (autowire) tanımı yapıldıysa, Symfony container'ı derlenemez (compile-time error). Bu durumda sistem çöker ve terminalden cp:module:deactivate komutunu bile çalıştıramayız.

Çözüm: ModuleActivateCommand sınıfına izole bir Dry-Run (Önizleme) kontrolü entegre ettik. Bir modül aktive edilmek istendiğinde şu akış izlenir:

  • Modül sınıfı geçici olarak active_modules.php dosyasına yazılır.
  • symfony/process bileşeni kullanılarak arka planda tamamen izole bir alt süreç (subprocess) başlatılır.
  • Bu alt süreçte sırasıyla cache:clear --no-warmup -> lint:yaml -> lint:container komutları koşturulur.
  • Eğer alt süreç 0 dışında bir exit code ile dönerse (hata varsa), ana süreç finally bloğunda dosyayı anında eski haline geri getirir, aktivasyonu iptal eder ve modülü kalıcı olarak karantinaya alır. Dosya asla bozuk haliyle kaydedilmez!

4. Savunma Hattı (Yeni): İzole Route Yükleyicisi

Standart Symfony'de bir modülün routes.yaml dosyasındaki tek bir syntax hatası tüm sistemi çökertir — üçüncü savunma hattı bunu aktivasyon anında yakalar ama sonradan bozulan bir dosyayı yakalamaz. Bu boşluğu kapatmak için SafeModuleRouteLoader eklendi: her modülün rotasını kendi izole try/catch bloğunda yükler. Hatalı modül sessizce atlanır, sistemin geri kalanı %100 ayakta kalır.

3. BÖLÜM: Hibrit Veri Modeli ve Yüksek Performanslı Flat Index Sistemi

İçerik yönetiminde iki klasik yaklaşım da sorunludur:

  • EAV (Entity-Attribute-Value) Modeli (Drupal): Her yeni alan için yeni bir veritabanı tablosu açılır. 15 özel alanı olan bir sayfayı çekmek için 15 JOIN sorgusu atılır; veritabanı kilitlenir.
  • Postmeta/Serialized Modeli (WordPress): Tüm özel alanlar tek bir metatablosunda satır satır tutulur. Filtreleme ve sıralama yapmak tam bir performans felaketidir.

CPalius Hibrit Veri Modeli: Sık sorgulanan, filtrelenen ve indekslenen temel alanları (ID, Başlık, Slug, Tür, Durum, Dil, Tarihler) gerçek veritabanı sütunları olarak tutuyoruz. İçeriğin kendisine ait tüm dinamik, esnek ve her projede değişebilecek alanları (Gövde metni, öne çıkan görsel, galeri alanları, SEO verileri) ise tek bir SQL json sütununda (data) saklıyoruz.

SQLite, MySQL ve Postgres Uyumluluğunda İndeksleme Çıkmazı

Dinamik JSON verilerini veritabanı düzeyinde sorgulamak isterseniz, SQLite, MySQL ve Postgres arasında tamamen farklı SQL sözdizimleri (syntax) kullanmanız gerekir. Ayrıca generated columns (üretilmiş kolonlar) üzerinde indeks oluşturmak Doctrine ORM şema araçlarıyla çalışırken son derece kırılgandır; Doctrine her şema güncellemesinde bu indeksleri silmeye çalışır.

Çözüm: NodeFieldIndex ve Dinamik İndeksleme Motoru

CPalius, bu sorunu Flat Field Index (Düz Alan Endeksi) tablosu ve bir Doctrine Event Listener ile çözer. queryable: true işaretlenen her alan otomatik olarak node_field_index tablosuna yansıtılır, ve ProcessWire'dan ilham alınan akıcı bir Selector API'siyle sorgulanır:

// Süper Akıcı (Fluent) Selector API Kullanımı
$nodes = $nodeRepository->findNodesBySelector( 'type=post, status=published, is_featured=1, limit=5, sort=createdAt:desc'
);

Bu tek satır arka planda tip-uygun kolonlara (value_string, value_int, value_decimal, value_datetime) sahip bir indeks tablosu üzerinden çalışır — hangi veritabanı motorunu kullanırsanız kullanın aynı DQL çalışır.

4. BÖLÜM: Çok Dilli Yapı ve Kompozit Benzersizlik Kısıtları

Çok dilli (i18n) yapı projelere sonradan eklendiğinde tüm veri modelini çökertir. CPalius, çoklu dili ilk günden çekirdeğin hücrelerine işlemiştir.

Kompozit Unique Constraints (Benzersizlik Güvencesi)

Çok dilli bir yapıda, klasik unique: true kısıtlamaları sistemi kilitler. Örneğin, Türkçe /tr/hakkimizda ile İngilizce /en/hakkimizda sayfalarının aynı anda var olabilmesi gerekir. Global tek bir slug unique kısıtlaması bunu engeller.

CPalius Çözümü: Veritabanı şemamızda kompozit (composite) benzersizlik kısıtları kullandık:

  • Yönlendirme Benzersizliği: Aynı slug sadece aynı dilde unique olmalıdır: UNIQUE(slug, locale).
  • Çeviri Grubu Benzersizliği: Aynı çeviri grubunda (translation group) aynı dilden sadece bir adet içerik bulunabilir: UNIQUE(translation_group_id, locale).

Ayrıca modüllerin kendi çevirilerini kendi dizinlerinde barındırabilmesi için TranslationFileLocator eklendi; bu sınıf dosyaları bulur ve bir Symfony Compiler Pass ile framework.translator.paths dizisine enjekte eder. Admin panelinden yapılan çeviri düzenlemeleri de atomik dosya yazma garantisiyle (.tmp dosyasına yazıp rename() ile değiştirme) diske işlenir, elektrik kesintisinde bile dosya yarım kalmaz.

5. BÖLÜM: "CMS"ten "Uygulama Framework'üne" Geçiş

CPalius sadece bir içerik yönetim sistemi (CMS) değildir; arkasında Oto Galeri, Turizm Acentası, Personel Yönetimi veya CRM/ERP sistemlerinin çalışabileceği esnek bir uygulama platformudur. Bu dönüşümü sağlamak için iki sınıf varlık tanımladık:

İki Sınıf Varlık (Content vs Business)

ÖzellikContent Entities (Node)Business Records (Resource)
Kavramsal KarşılıkSayfa, Yazı, İlan Vitrini, BlogAraç, Fatura, Rezervasyon, Personel
ÖzellikleriSlug var, çoklu dil var, yayın durumu var, SEO varSlug yok, dil yok, yayın yok, durum makinesi (workflow) var
Ortak PaydaAynı Yetenek (Capability) modeli, aynı Twig bileşenleri, aynı CLI yönetimi, aynı Config Sync

#[CpResource] Devrimi

Geliştiricinin iş süreçlerini kodlamasını saniyeler seviyesine indirmek için özel bir PHP Attribute yapısı tasarladık. Tek bir anotasyon ile veritabanındaki ham bir Doctrine sınıfını platformun tüm güçleriyle birleştiriyoruz:

#[CpResource( name: 'vehicle', module: 'oto-galeri', capabilities: ['create', 'edit', 'delete', 'view'], auditable: true, multiTenant: true, workflow: 'vehicle_lifecycle'
)]
#[ORM\Entity]
class Vehicle
{ #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; #[ORM\Column(length: 20)] private ?string $plate = null; #[ORM\Column(length: 50)] private ?string $brand = null; #[ORM\Column(type: 'integer')] private ?int $price = 0; // Kuruş bazında saklanır (Float asla!)
}

Bu tek nitelik (#[CpResource]) sayesinde:

  • Rol ve yetenek matrisine (vehicle.create, vehicle.edit vb.) dinamik yetenekler otomatik kaydolur.
  • Otomatik CRUD formları ve liste ekranları bu tanıma göre dinamik olarak türetilir.
  • SaaS projeleri için çoklu kiracı (multiTenant: true) izolasyonu arka planda otomatik uygulanır.
  • Entity üzerinde yapılan tüm değişiklikler anlık olarak sürüm geçmişine (audit log) kaydedilir.

Not: Bu altyapı (registry, capability üretimi, multiTenant/auditable bayrakları) tamamen hazır ama henüz somut bir business entity ile canlıda kullanılmıyor — yol haritasındaki bir sonraki adım bu.

6. BÖLÜM (Yeni): Platform Genişletilebilirlik Katmanı

İlk whitepaper'dan bu yana çekirdeğe dört yeni genişletme omurgası eklendi; hepsi aynı "Core Never Dies" zırhını taşıyor:

REST API Gateway

Tek bir /api/{path} joker route'u, #[CpApi] attribute'lu servis metotlarını derleme zamanında (ApiRegistrationPass) eşleştirir. Kimlik doğrulama X-CP-API-KEY header'ı ile fail-closed çalışır — anahtar geçersizse hedef metot hiç çağrılmaz. Her endpoint kendi try/catch zırhında izole edilir; çöken bir uç yalnızca kendini etkiler.

// Modül içerisindeki tertemiz uç nokta
#[CpApi(path: '/blog/posts', methods: ['GET'], public: false)]
public function getPosts(Request $request): JsonResponse
{ // İş mantığı...
} // ApiGatewayController'daki fail-closed zırhı
if (!$endpoint['definition']['public'] && !$this->hasValidApiKey($request)) { return new JsonResponse(['error' => 'Unauthorized'], Response::HTTP_UNAUTHORIZED);
}

İzole Hook Sistemi

Cotonti tarzı flat-file Hooks/{hook_point}.php dosyaları izole bir Closure içinde çalışır — dış scope'a değişken sızıntısı imkansızdır. Symfony tarzı #[CpHook] attribute'lu servisler ise HookRegistrationPass ile toplanır. Çöken hook sayfayı asla 500'e düşürmez, karantina günlüğüne yazılır.

Birleşik Cron / Otomasyon Motoru

CronManager üç paralel kaynağı — veritabanındaki cp_cron_jobs, #[CpCronJob] işaretli servisler ve flat-file Hooks/cron.{job}.php dosyalarını — tek listede birleştirir. Görevler CronCommandWhitelist ile yalnızca cp:* önekli komutlara izin veren izole alt-süreçlerde çalıştırılır.

// cp-content/modules/Blog/Cron/PublishScheduledPostsTask.php
final class PublishScheduledPostsTask
{ public function __construct( private readonly EntityManagerInterface $entityManager, private readonly NodeRepository $nodeRepository, ) {} #[CpCronJob(schedule: '*/5 * * * *', name: 'blog.publish_scheduled')] public function execute(): string { $dueNodes = $this->nodeRepository->findDueScheduledNodes(); if ($dueNodes === []) { return 'Yayın zamanı gelmiş içerik yok.'; } foreach ($dueNodes as $node) { $node->publish($node->getPublishedAt()); } $this->entityManager->flush(); return sprintf("%d içerik yayına alındı.", count($dueNodes)); }
}

Modülden Bağımsız Plugin Katmanı

Modüllerden ayrı ikinci bir genişletme noktası: PluginInterface işaretli servisler PluginRegistry tarafından toplanır, aktiflik durumu PluginToggleRepository ile veritabanında tutulur. Bir modül kendi içinde widget/sidebar gibi opsiyonel alt-özellikleri, modülün kendisinden bağımsız açıp kapatabilir.

Bunlara ek olarak DBAL Middleware seviyesinde çalışan bir N+1 muhafızı (QueryCounterConnection + TableParser zinciri, tablo bazlı sorgu sayacı tutar), talep edildiğinde tek sorguyla ayar çeken lazy-load bir SettingsRegistry (#[CpSetting]), ve gelecekteki async kuyruklar için kurulu ama henüz transport'a bağlanmamış bir symfony/messenger altyapısı da eklendi.

7. BÖLÜM (Yeni): AACP — Admin Kontrol Paneli

İlk whitepaper'ın roadmap bölümünde vaat edilen Safe Mode / Kurtarma Konsolu artık gerçek: veritabanı tamamen çökse dahi ayakta kalan, token tabanlı bir kurtarma arayüzü dahil, AACP tam bir sistem yönetim merkezine dönüştü.

Safe Mode & Kurtarma Konsolu

/aacp/recovery ucu security.yaml'da bilinçli olarak herkese açık (PUBLIC_ACCESS) bırakılmıştır; yetkilendirmeyi kendisi .env'deki AACP_RECOVERY_TOKEN ile hash_equals() (timing-safe) karşılaştırması yapar. Hiç Doctrine sorgusu çalıştırmaz — veritabanı tamamen çökmüşken bile devrededir. Token boşsa kapı tamamen kapalıdır (fail-safe).

Canlı Sistem Monitörü

/aacp/system ve /aacp/system/metrics JSON ucu; load average, memory, OPcache hit-rate, veritabanı bağlantı durumu ve Messenger kuyruk durumunu htop tarzı canlı olarak raporlar.

Karantina Ekranı & Cache Rebuild Konsolu

/aacp/quarantine — module_quarantine.log dosyasını okuyup listeleyen salt-okunur panel. /aacp/system/cache-rebuild/* ise Symfony cache, OPcache reset ve Tailwind asset rebuild işlemlerini aynı CSRF token'ı paylaşan üç ayrı AJAX ucu üzerinden tetikler.

Lokalizasyon Paneli & Performans Yönetimi

TranslationManager web üzerinden çeviri düzenlemeyi sağlar; ayrı bir Performans Yönetimi ekranı ise Redis, Memcached, Varnish ve Nginx PageSpeed için canlı bağlantı testleri çalıştırır — "aktif" bayrağı yalnızca test başarılıysa set edilir.

8. BÖLÜM (Yeni): İlk Parti Referans Modüller

Blog, Medya ve Menü modülleri, platformun her genişletme noktasını (API, Hook, Cron, Plugin, Settings) uçtan uca kullanan referans implementasyonlar olarak eklendi:

  • Blog Modülü: Node::type='post' üzerine kurulu tam blog sistemi. Kategori/etiket yönetimi, zamanlanmış yayınlama, GET /api/blog/posts REST ucu, sidebar hook'u ve Schema.org (BlogPosting) JSON-LD üretimi bir arada.
  • Media Modülü: AssetManager ve bağımsız Asset entity'si üzerine kurulu medya kütüphanesi + görsel seçici (picker) admin arayüzü. Flysystem depolama katmanı üzerinde sha256 tabanlı deduplikasyon.
  • Menu Modülü: WordPress tarzı sürükle-bırak frontend menü yönetimi. Menu/MenuItem entity'leri, soft-delete uyumu için Node'a gevşek referansla (FK değil) bağlanır.

9. BÖLÜM: Güvenlik ve Performans Anayasası

Güvenlik ve performans, projenin son aşamasında "üzerine eklenen" birer cila değildir; sistemin en temel yapı taşlarıdır.

1. Yetenek Tabanlı Erişim Kontrolü (CBAC)

Standart Symfony ROLE_ADMIN veya ROLE_USER yaklaşımları hantaldır ve sonradan genişletilemez. CPalius'ta kodun hiçbir yerinde rol ismi kontrol edilmez; her zaman dinamik olarak üretilen yetenekler (capabilities) kontrol edilir:

// Doğru Yaklaşım
$this->denyAccessUnlessGranted('system.module.manage'); // Yanlış Yaklaşım
if ($this->getUser()->getRole() === 'ROLE_ADMIN') { ... }
  • Roller = Config (YAML): Rol tanımları ve bunların yetenek matrisleri cp-content/config/sync/ altında YAML dosyalarında tutulur ve Git ile taşınır.
  • Kullanıcılar = Content (DB): Gerçek kullanıcı kayıtları ise veritabanında kalır, asla dışa aktarılmaz (export edilmez).

2. SaaS Veri Sızıntısı Koruması (Automatic Tenant SQLFilter)

Çok kiracılı (SaaS) sistemlerde en büyük güvenlik açığı, yazılımcının bir sorgunun sonuna WHERE tenant_id = ? yazmayı unutmasıyla yaşanır. CPalius'ta bu ihtimal platform seviyesinde yok edilmiştir: Doctrine SQLFilter otomatik olarak her sorguya tenant kısıtını enjekte eder.

// cp-core/src/Core/Database/TenantFilter.php
class TenantFilter extends SQLFilter
{ public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string { if ($targetEntity->getReflectionClass()->hasAttribute(CpResource::class)) { return sprintf('%s.tenant_id = %s', $targetTableAlias, $this->getParameter('current_tenant_id')); } return ''; }
}

3. N+1 Sorgu Muhafızı

DBAL Middleware zincirinde (QueryCounterConnection, Driver, Statement, TableParser) tablo bazlı sorgu sayacı tutulur; tek bir HTTP isteğinde aynı tabloya atılan sorgu sayısı belirlenen limiti (örn: 10 sorgu) aşarsa sistem doğrudan MaxQueriesExceededException fırlatır. Geliştirici bu hatayı lokalde çözmeden kodu canlıya alamaz.

4. Voter-to-SQL Dönüşümü (Yeni)

Liste ekranlarındaki PHP tabanlı Voter kontrollerinin yarattığı N+1 bellek krizini ortadan kaldıran QueryScopeApplier eklendi. Kullanıcının .own veya .any yetenekleri, sorgu veritabanına gitmeden önce doğrudan Doctrine QueryBuilder üzerinden SQL WHERE koşuluna çevrilir.

5. Zero-Trust Payload Validasyonu ve Core-Level XSS Sanitization (Yeni)

Dış dünyadan gelen hiçbir HTTP yüküne güvenmeyen strict-type DTO mimarisi; veriler controller'lara ulaşmadan önce Symfony Validator ile otonom olarak sterilize edilir. Ayrıca RichTextSanitizer ile veritabanına kaydedilecek her zengin metin çekirdek seviyesinde temizlenir — XSS koruması opsiyonel bir eklenti değil, çekirdeğin bir parçası.

6. Lock-Free Yüksek Eşzamanlılık (Yeni)

Geleneksel tablo kilitleme (table-lock) krizlerini ortadan kaldıran Optimistic Locking omurgası; aynı anda gerçekleşen binlerce veri mutasyonu, atomik transaksiyonlarla deadlock olmadan izole edilir.

10. BÖLÜM: Gelecek Yol Haritası ve Teknik İstişare (RFC)

İlk whitepaper'da vaat edilen üç maddeden Safe Mode tamamlandı, Asset Sistemi kısmen tamamlandı (Flysystem katmanı hazır). Sıradaki sprintlerde şunlar var:

  • Workflow & State Machine: Fatura, araç, rezervasyon gibi iş kayıtlarının geçiş süreçlerini (draft → preparation → sold) YAML tanımlarıyla yöneten ve her geçişi otomatik audit log'a bağlayan mekanizma. CpResource::$workflow alanı zaten deklare edilmiş durumda — sıradaki adım gerçek transition/guard motorunu bağlamak.
  • LiipImagineBundle Entegrasyonu: Flysystem tabanlı Asset katmanı hazır; sırada URL üzerinden anlık görsel türetme (resize/crop/thumbnail pipeline) var.
  • Somut Resource & Audit Log: #[CpResource] altyapısı tamamen hazır ama henüz hiçbir concrete entity kullanmıyor. Sıradaki adım: ilk Business Record örneği (örn. Vehicle) ve gerçek bir AuditLog tablosuna bağlanması.
  • Messenger Async Kuyruk: symfony/messenger kurulu ve AACP Sistem Monitörü'nde izlenebiliyor; sıradaki adım gerçek bir transport (Doctrine/Redis) bağlayıp e-posta ve bildirim işlemlerini asenkron kuyruğa taşımak.

💬 Soru ve Görüşleriniz (Topluluk İstişaresi)

Bu mimariyi daha da kusursuzlaştırmak için siz değerli geliştiricilerden şu konularda geri bildirim bekliyoruz:

  • Flat Field Index Modeli: SQLite, MySQL ve Postgres uyumluluğu için tasarladığımız bu model sizce devasa veri hacimlerinde nasıl bir performans gösterir? İndeks tablosunun partition edilmesi gerekir mi?
  • Config Sync Yaklaşımı: Drupal'ın config sync sistemini Symfony'nin TreeBuilder yapısıyla taklit etme fikrimiz hakkındaki düşünceleriniz nelerdir?
  • Zero Node.js Israrı: Geliştirici ve admin panelinde AssetMapper + Standalone Tailwind kullanma kararımız, gelecekte çok karmaşık frontend bileşenleri yazarken bizi kısıtlar mı?

Fikirlerinizi, eleştirilerinizi ve mimari önerilerinizi heyecanla bekliyoruz! CPalius, topluluğun gücüyle en sağlam uygulama framework'üne dönüşecek.

Ekran Görüntüleri

comments[] (0)

Henüz yorum yok. İlk yorumu siz yazın.

Yorum Yaz

stats
site.metrics
940bugün ziyaretçi
1515bugün görüntülenme
120748toplam ziyaretçi
227421toplam görüntülenme
165içerik
963yorum