Ders 18 / 21
Dosya Depolama
Disk soyutlaması: aynı arayüz arkasında yerel disk ve bellek diski; aynı yükleme çağrısının iki diskte koşması, yapılandırmada adı geçmeyen diskin bağlanmaması ve disk değişiminin yanıta değil dosyanın ömrüne yansıması.
Yayın Güncelleme
Bu derste
- Yerel disk ile bellek diskini aynı arayüz arkasında birbirinin yerine kullanır
- Aynı yükleme çağrısının iki diskte de koştuğunu ve denetleyicinin diskten habersiz kaldığını ölçer
- Yapılandırmada adı geçmeyen diskin yola bağlanmadığını ve disk değişiminin dosyanın ömrünü değiştirdiğini gösterir
İçindekiler
Önbellekleme dersinde saklanan değer, kaynağındaki değişiklikten habersiz kaldığı sürece bayat kaldı. O ders değerleri bir dizide tuttu; bir dosya ise değerden farklı bir şeydir: boyutu büyüktür, yanıtın dışında yaşamalıdır ve nereye yazıldığı uygulamanın yaşamı boyunca değişebilir. Bir geliştirici yüklenen dosyayı bugün çalışma dizinine yazar, yarın nesne depolama diye bilinen ayrı bir hizmete taşımak ister. Bu ders şunu sorar: depolamanın yeri değiştiğinde yükleme kodu da değişir mi?
Disk bu derste bir dosyanın yazılıp okunabildiği yerin adıdır; ürün adı değildir. Disk soyutlaması denetleyicinin yalnız yaz, oku ve var sözleşmesini bilmesi, dosyanın nereye gittiğini bilmemesi demektir. Sözleşmeyi bir arayüz (interface) taşır ve her disk onu gerçekler. Dersin iki diski vardır: çalışma dizinindeki bir klasöre yazan yerel disk ve dosyaları bir dizide tutan bellek diski. Bellek diski gerçek bir nesne depolamanın modelidir: dosya bir yol adı altında saklanır, ama dizin ağacı, izin ve sürücü yoktur; yalnız ad ve içerik vardır. Hangi diskin kullanılacağını bir yapılandırma söyler: adlar diske eşlenir ve varsayılan ad ayrıca seçilir.
US6. Yerel disk, çalışma dizininin altında bu ders için açılan bir klasördür; ders bitince silinir ve yol çıktıya basılmaz. Bellek diski bir dizidir ve süreç bitince kaybolur. Bulut depolama, ağ gecikmesi, kimlik bilgisi, eşzamanlı yazma ve dosya izinleri modellenmez; bellek diski yalnızca nesne depolamanın “ad altında içerik” modelini taşır. Bir dosyanın yazılması bir yöntem çağrısıdır ve başarısız yazma modellenmez.
US7. Çözümleme yapılandırmadaki adlarla sınırlıdır. İstenen ad yapılandırmada yoksa varsayılan diske düşülmez; Depolama bir hata verir ve denetleyici bu hatayı 500 yanıtına çevirir. Sessiz bir yedek disk, dosyanın beklenmedik bir yerde yaşamasına yol açacağı için bu derste kasıtlı olarak yoktur.
Ölçüm Çekirdeği
Kurs boyunca her derste aynı ölçüm kullanılır. kostu.php beklenen bir parçanın bu istekte koşup koşmadığını kaydeder; dosya her derste birebir verilir ve çıktı bloğu yoktur, yalnız diske yazılır. Gunluk üç liste tutar: isteğin yoluna konanlar, istek içinde çağrılanlar ve isteğin dışına bırakılanlar. sinifla() bunlara sırayla bakar. Karar sırası sinifla()’nın kendisidir: koştu, ertelendi, atlandı, bağlanmadı. Parçayı geliştirici adlandırır; ölçülen, geliştiricinin bu istekte koşmasını beklediği parçalardır. Bu derste yola() çağrısı bir diskin yapılandırmadan çözüldüğünü, kostu() çağrısı ise o diskin yaz() yönteminin çalıştığını işaretler.
<?php // kostu.php — beklenen bir parça bu istekte koştu mu? declare(strict_types=1); final class Gunluk { public static array $yol = []; // isteğin yoluna konan parçalar public static array $kostu = []; // istek içinde çağrılanlar public static array $ertelenen = []; // isteğin dışına bırakılanlar public static function yeniIstek(): void { self::$yol = self::$kostu = self::$ertelenen = []; } public static function yola(string $parca): void { self::$yol[] = $parca; } public static function kostu(string $parca): void { self::$kostu[] = $parca; } public static function ertele(string $parca): void { self::$ertelenen[] = $parca; } } function sinifla(string $parca): string { return match (true) { in_array($parca, Gunluk::$kostu, true) => 'koştu', in_array($parca, Gunluk::$ertelenen, true) => 'ertelendi', in_array($parca, Gunluk::$yol, true) => 'atlandı', default => 'bağlanmadı', }; }
Disk Arayüzü ve İki Disk
depolama.php dört şey tanımlar. Disk arayüzü sözleşmeyi verir. YerelDisk dosyayı çalışma dizinindeki klasöre file_put_contents ile yazar; temizle() yöntemi arayüzün parçası değildir ve yalnız dersin kendi artığını silmek içindir. BellekDiski aynı sözleşmeyi bir diziyle gerçekler. Depolama adlarla diskleri eşler ve disk() çağrısında adı çözümler: ad verilmezse varsayılan kullanılır, ad yapılandırmada yoksa InvalidArgumentException fırlatılır. Son olarak YuklemeDenetleyicisi bir dosyayı ve içeriğini alır, isteğin yoluna yukle parçasını koyar, kendi işaretini bırakır ve dosyayı seçilen diske yazar.
Denetleyicinin gövdesinde hangi diskin kullanıldığını söyleyen tek bir ifade yoktur. disk($disk) çağrısı bir Disk döndürür, yaz() yöntemi sözleşmeden gelir. Disk değiştiğinde değişen şey, Depolama nesnesine hangi adın verildiğidir; denetleyici bu bilginin yalnız dışarıdan geldiğini görür.
<?php // depolama.php — disk arayüzü, yerel disk, bellek diski ve yapılandırma declare(strict_types=1); require_once 'kostu.php'; interface Disk { public function yaz(string $yol, string $icerik): void; public function oku(string $yol): string; public function var(string $yol): bool; } final class YerelDisk implements Disk { public function __construct(private string $kok) { if (!is_dir($kok)) mkdir($kok, 0777, true); } public function yaz(string $yol, string $icerik): void { Gunluk::kostu('yerel'); file_put_contents($this->kok . '/' . $yol, $icerik); } public function oku(string $yol): string { return file_get_contents($this->kok . '/' . $yol); } public function var(string $yol): bool { return is_file($this->kok . '/' . $yol); } public function temizle(): void { foreach (glob($this->kok . '/*') as $dosya) unlink($dosya); rmdir($this->kok); } } final class BellekDiski implements Disk { private array $dosyalar = []; public function yaz(string $yol, string $icerik): void { Gunluk::kostu('bellek'); $this->dosyalar[$yol] = $icerik; } public function oku(string $yol): string { return $this->dosyalar[$yol]; } public function var(string $yol): bool { return isset($this->dosyalar[$yol]); } } final class Depolama { private array $diskler = []; public function __construct(private string $varsayilan) {} public function ekle(string $ad, Disk $disk): void { $this->diskler[$ad] = $disk; } public function disk(?string $ad = null): Disk { $ad ??= $this->varsayilan; if (!isset($this->diskler[$ad])) { throw new InvalidArgumentException("disk yapilandirmada yok: $ad"); } Gunluk::yola($ad); return $this->diskler[$ad]; } } final class YuklemeDenetleyicisi { public function __construct(private Depolama $depolama) {} public function yukle(string $dosya, string $icerik, ?string $disk = null): array { Gunluk::yeniIstek(); Gunluk::yola('yukle'); Gunluk::kostu('yukle'); try { $this->depolama->disk($disk)->yaz($dosya, $icerik); } catch (InvalidArgumentException $hata) { return [500, 'disk yok']; } return [201, 'yuklendi']; } }
Aynı Çağrı İki Diskte Koştu
Sürücü aynı yukle() çağrısını üç kez yapar: ad verilmeden, bellek adıyla ve yapılandırmada adı geçmeyen uzak adıyla. Her çağrıdan sonra üç parçanın sınıfı yazılır. Birinci çağrıda varsayılan disk yerel’dir; denetleyici çağrılırken disk adını bilmez, yine de dosya yerel diske gider. İkinci çağrıda aynı denetleyici, aynı kod, aynı dosya adı ve aynı içerikle bellek diskine yazar. İki çağrı da 201 döndürür ve denetleyicide tek satır değişmemiştir.
<?php // disk.php — aynı yukle() çağrısı iki diskte, üçüncü disk yapılandırmada yok require_once 'depolama.php'; $yerel = new YerelDisk('depo-ders06'); $depolama = new Depolama('yerel'); $depolama->ekle('yerel', $yerel); $depolama->ekle('bellek', new BellekDiski()); $denetleyici = new YuklemeDenetleyicisi($depolama); foreach ([null, 'bellek', 'uzak'] as $ad) { $yanit = $denetleyici->yukle('rapor.txt', 'merhaba', $ad); printf("yukle(disk: %s) -> %d %s\n", $ad ?? 'varsayılan', $yanit[0], $yanit[1]); foreach (['yerel', 'bellek', 'uzak'] as $parca) printf(" %-6s %s\n", $parca, sinifla($parca)); } echo 'yerel diskte okunan: ', $yerel->oku('rapor.txt'), "\n"; $yerel->temizle();
yukle(disk: varsayılan) -> 201 yuklendi yerel koştu bellek bağlanmadı uzak bağlanmadı yukle(disk: bellek) -> 201 yuklendi yerel bağlanmadı bellek koştu uzak bağlanmadı yukle(disk: uzak) -> 500 disk yok yerel bağlanmadı bellek bağlanmadı uzak bağlanmadı yerel diskte okunan: merhaba
Birinci çağrıda yerel koştu, çünkü hem yola konmuş hem çağrılmıştır; bellek ise yapılandırmada tanımlı olduğu hâlde bu istekte çözülmediği için bağlanmadı. İkinci çağrıda roller yer değiştirir. Bu iki çıktı birlikte dersin ölçü sınıfını verir: aynı yaz() çağrısı iki diskte de koştu. Ölçüm “yazıldı” demez, “çalıştı” der; dosyanın gerçekten oraya ulaşıp ulaşmadığını sürücünün son satırı, yerel diskten okunan merhaba, ayrıca gösterir. Üçüncü çağrının sonucu ise bir sonraki bölümün konusudur.
Okuma Tarafı da Aynı Sözleşmeden Geçer
Yazma tarafı ölçüldü; okuma tarafında da aynı soru sorulabilir. okuma.php iki diske aynı iki satırlık içeriği yazar, sonra her diskten içeriği okur ve iki ayrı dosya adı için var() çağırır: yazılmış olan ozet.txt ve hiç yazılmamış eksik.txt. Arayüz yalnız bayt sayısını ve bir doğru ya da yanlış değerini vaat eder; ölçüm, iki diskin bu vaadi aynı biçimde tutup tutmadığına bakar.
<?php // okuma.php — iki diskte aynı yaz, oku ve var çağrıları require_once 'depolama.php'; $diskler = ['yerel' => new YerelDisk('depo-ders06c'), 'bellek' => new BellekDiski()]; foreach ($diskler as $ad => $disk) { $disk->yaz('ozet.txt', "satir bir\nsatir iki"); printf("%-6s oku: %d bayt, ozet.txt var: %s, eksik.txt var: %s\n", $ad, strlen($disk->oku('ozet.txt')), $disk->var('ozet.txt') ? 'evet' : 'hayır', $disk->var('eksik.txt') ? 'evet' : 'hayır'); } $diskler['yerel']->temizle();
yerel oku: 19 bayt, ozet.txt var: evet, eksik.txt var: hayır bellek oku: 19 bayt, ozet.txt var: evet, eksik.txt var: hayır
İki satır aynıdır: içerik 19 bayt olarak döner, yazılan dosya vardır ve yazılmayan dosya yoktur. var() yöntemi var olmayan bir dosya için hata atmaz, yanlış döndürür; böylece okumadan önce varlığı sormak iki diskte de güvenlidir. Aynı sözleşmenin dışında kalan şey oku() yönteminin var olmayan bir dosyada ne yaptığıdır: arayüz bunu tanımlamaz ve iki disk bu durumda farklı davranabilir. Bu yüzden ders, okumadan önce var() sorulmasını sözleşmenin parçası sayar ve bu varsayımı yazılı tutar.
Karşıt: Yapılandırmada Adı Geçmeyen Disk Bağlanmadı
Üçüncü çağrıda uzak adı verildi. Depolama::disk() adı yapılandırmada bulamadı, hatayı yola işaret koymadan önce fırlattı, denetleyici de hatayı 500 yanıtına çevirdi. Çıktının son bölümünde üç parçanın hiçbiri koşmadı: yerel ve bellek bu istekte hiç çözülmediği için, uzak ise yapılandırmada olmadığı için yola bile girmedi. Üçü de bağlanmadı.
Bunun neden atlandı olmadığını görmek önemlidir. Bir parça, yolda olduğu hâlde çağrılmadığında atlanır. uzak bu istekte yola hiç konmadı; çünkü çözümleme, ad geçerli olmadan işaret bırakmaz. Yani sapma, kodun bir dalı atlanarak değil, bağlantının kendisi kurulamadığı için oluştu. Aynı hatanın “disk var ama yazma başarısız oldu” biçimi bu dersin modelinde yoktur; orada yola işareti bırakılır ve yaz() içindeki kostu işareti eksik kalırdı, bu durum atlandı olarak okunurdu.
Karşıtın pratik anlamı bir yapılandırma hatasının sessizce başka yere yazmak yerine yüksek sesle başarısız olmasıdır. Dosya hiçbir diskte yoktur ve istemci 500 alır; sessiz bir varsayılan diske düşmek ise yüklenen dosyanın beklenmedik bir yerde yaşamasına yol açardı. Burada ölçüm yalnız “bir parça bağlanmadı” der; hatanın bir yapılandırma hatası mı, bir yazım hatası mı olduğunu söylemez.
Sınırlayıcı Ölçüm
Önceki iki bölüm “denetleyici diskten habersiz” diyor; bu bölüm bunun neyi kapsamadığını ölçer. Disk değişince denetleyici değişmedi, ama dosyanın ömrü değişti. kalicilik.php iki diski aynı iş için kullanır: dosyayı yazar, sonra aynı türden yeni bir disk örneği kurar ve dosyanın orada olup olmadığına bakar. Yeni örnek, yeni bir istekte yeniden kurulmuş bir bileşeni temsil eder.
<?php // kalicilik.php — aynı arayüz, farklı ömür require_once 'depolama.php'; foreach (['yerel', 'bellek'] as $tur) { $kur = fn(): Disk => $tur === 'yerel' ? new YerelDisk('depo-ders06b') : new BellekDiski(); $birinci = $kur(); $birinci->yaz('not.txt', 'kalici mi'); $ikinci = $kur(); // yeni örnek: aynı arayüz, sıfırdan kurulmuş printf("%-6s birinci var: %s, yeni örnekte var: %s\n", $tur, $birinci->var('not.txt') ? 'evet' : 'hayır', $ikinci->var('not.txt') ? 'evet' : 'hayır'); } (new YerelDisk('depo-ders06b'))->temizle();
yerel birinci var: evet, yeni örnekte var: evet bellek birinci var: evet, yeni örnekte var: hayır
Yerel diskte yeni örnek dosyayı görür, çünkü dosya klasörde durur ve iki örnek aynı klasöre bakar. Bellek diskinde yeni örnek boştur; dosya birinci örneğin dizisinde kalmıştır. Arayüz iki durumda da aynıdır: var() yöntemi iki diskte çağrılır ve ikisi de bir doğru ya da yanlış döndürür. Fark arayüzün dışındadır, diskin ömründedir.
Bunun sonucu, Önbellekleme dersinin Sonraki Adım’ında sorulan soruya yanıttır: disk değişince yanıt değişmedi, ama yanıtın dayandığı dosyanın bir sonraki istekte var olup olmadığı değişti. Soyutlama çağrı biçimini örter, çağrının kalıcılık sözleşmesini örtmez. Denetleyicinin yükleme kodu disk değişiminden etkilenmedi; ancak yüklenen dosyayı bir sonraki istekte okuyan kod bellek diskiyle yanlış sonuç alırdı. Bu ölçüm yalnız iki diskin kalıcılığını karşılaştırır; gerçek nesne depolamanın tutarlılık ve gecikme davranışını, ağ kopmasını ya da büyük dosyayı ölçmez.
Özet
- Bu dersin ölçü sınıfı koştu: aynı
yukle()çağrısı yerel diskte de bellek diskinde deyaz()yöntemini çalıştırdı ve denetleyici disk değişiminden habersiz kaldı. Diskarayüzüyaz,okuvevarsözleşmesini verir;Depolamaadları diske eşler ve varsayılanı ayrı tutar.- Karşıt: yapılandırmada adı geçmeyen
uzakdiski yola girmedi ve bağlanmadı; denetleyici 500 döndürdü, dosya hiçbir diske gitmedi. - Sınırlayıcı ölçümde yeni disk örneği yerel diskteki dosyayı gördü, bellek diskindeki dosyayı görmedi; soyutlama çağrı biçimini örter, kalıcılığı örtmez.
- Disk değişince denetleyici değişmez, ama dosyayı sonradan okuyan kodun varsayımı değişebilir.
Sonraki Adım
Yüklenen dosya artık bir diske yazılıyor ve hangi diske yazıldığı denetleyiciyi ilgilendirmiyor. Ama bu işlerin hepsi hâlâ bir isteğin içinde, yanıt gelmeden önce yapılıyor. Peki bazı işler hiçbir isteğe bağlı olmadan, her dakika ya da her gün kendiliğinden tekrar etmeliyse, kim çalıştıracak ve istek açısından bu iş ne zaman koşmuş sayılacak? Zamanlanmış Görevler dersi yinelenen bir görevi tanımlar, zamanlayıcının tikinde koşturur ve isteğin bu görevi beklemeden yanıt verip vermediğini ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.