Ders 01 / 12
Sınıflar ve Nesneler
Bir sınıfın içindeki ad değiştiğinde onu kullanan kodun ne yaşadığı: özel özellik, açık özellik ve public özelliğe yazan kullanan; bir değişikliğin nereye ulaştığını ölçen dört sınıf.
Yayın Güncelleme
Bu derste
- Bir sınıfın özellik, yöntem ve yapıcı biçimlerini ve nesnelerin ayrı durum taşıdığını gösterir
- Bir tanım değişikliğinin kullananlara ulaşıp ulaşmadığını önce ve sonra koşumuyla sınıflandırır
- Özel özelliğin adının değişmesinin kullananı etkilemediğini, açık özelliğin değişmesinin etkilediğini ölçer
- Public özelliğe yazan kullananın ad değişince durmadığını, yalnız bir E_DEPRECATED bildirdiğini doğrular
İçindekiler
Önceki kurs PHP’nin dilini tek bir dosya içinde ölçtü: bir değerin tipi ne zaman değişir, bir hata hangi kanaldan duyulur. Bu kurs dosyanın dışına çıkıyor. Gerçek bir programda tanımlar bir dosyada, onları kullanan kod başka dosyalarda durur ve tanım bir gün değişir. Soru şudur: bu değişiklik nereye ulaşıyor? Kurs bu soruyu her düzenek için aynı yöntemle sorar ve yanıtı dört addan biriyle verir.
Yerinde: değişiklik tanımın içinde kaldı, kullananlar düzenlenmeden aynı sonucu verdi. Bağlantıda: değişiklik tek bir bağlantı noktasında karşılandı, bir yükleyicide ya da bir kurulum dosyasında. Kıran: değişiklik kullananlara ulaştı ve onları durdurdu, çıkış kodu 0 değil. Örtük: değişiklik kullananlara ulaştı, onlar çalıştı ama sonuç değişti. Kıran ve örtük sınıflar M12/K01’in sinyal ayrımından farklıdır; orada bir aksaklığın hangi kanaldan duyulduğu ölçülmüştü, burada iki koşumun sonucu karşılaştırılacak. M11/K03’te ölçülen de başkaydı: orada bir verinin değişikliği, burada bir tanımın değişikliği söz konusu. Kurs boyunca on iki değişiklik ölçülecek.
Sınıf, Özellik ve Yöntem
Sınıf (class), nesnelerin kalıbıdır. İçinde özellikler (properties) ve yöntemler (methods) bulunur. Özellik nesnenin taşıdığı veridir, yöntem nesnenin yapabildiği iştir. new ile sınıftan bir nesne, yani bir örnek (instance) kurulur ve her örnek kendi özellik değerlerini taşır. Bir yöntemin içinden kendi nesnesine $this ile ulaşılır. Aşağıdaki sınıf, bu kursun dosya adı kuralını da gösterir: sınıf dosyasının adı sınıfın adıyla aynıdır, sınıflar StudlyCaps, yöntem ve özellikler camelCase yazılır.
<?php // Hesap.php — ozellik, yontem ve yapici tasiyan bir sinif class Hesap { public function __construct(private int $bakiye = 0) {} public function yatir(int $tutar): void { $this->bakiye += $tutar; } public function bakiye(): int { return $this->bakiye; } }
__construct yapıcı işlevdir (constructor): new çağrısında çalışır. Parametrenin önündeki private sözcüğü parametreyi aynı anda özellik de yapar; ayrı bir özellik bildirimi ve atama satırı gerekmez. private bir görünürlüktür (visibility): özelliğe yalnız sınıfın kendi yöntemleri ulaşabilir. Bu kapsülleme (encapsulation) demektir. Sınıf dışarıya veriyi değil, yöntemleri açar.
<?php // hesap_kullan.php — iki nesne iki ayri durum tasir; ozel ozellige disaridan erisilemez require 'Hesap.php'; $birinci = new Hesap(10); $ikinci = new Hesap(); $birinci->yatir(50); $ikinci->yatir(5); echo $birinci->bakiye(), ' ', $ikinci->bakiye(), "\n"; try { echo $birinci->bakiye; } catch (Error $hata) { echo get_class($hata), "\n"; }
60 5 Error
NM1. İki nesne aynı sınıftan kuruldu ve yatırılan tutarlar birbirine karışmadı: 60 ve 5. Özel özelliğe yöntem olmadan ulaşan satır Error fırlattı. Kullanan koda açılan tek yüzey yöntemlerdir; özelliğin adı bu yüzeyin parçası değildir. Bu gözlem, aşağıdaki ölçümün neden yerinde çıkacağının gerekçesidir.
Yapıcıda Özellik, readonly ve Üç Görünürlük
Yapıcı parametresinin önüne görünürlük yazmak (constructor promotion), uzun biçimin kısaltmasıdır: özellik bildirimi ve $this->bakiye = $bakiye ataması kendiliğinden yapılır. İki biçim aynı nesneyi üretir.
<?php // Esdeger.php — uzun bicim ve kisa bicim ayni nesneyi kurar class Uzun { private int $bakiye; public function __construct(int $bakiye = 0) { $this->bakiye = $bakiye; } public function bakiye(): int { return $this->bakiye; } } class Kisa { public function __construct(private int $bakiye = 0) {} public function bakiye(): int { return $this->bakiye; } } var_dump((new Uzun(7))->bakiye() === (new Kisa(7))->bakiye());
bool(true)
Bir özellik kurulduktan sonra değişmeyecekse readonly yazılır: değer yapıcıda bir kez atanır, sonra hiçbir yerden değiştirilemez. Okuma açık kalır, yazma Error doğurur.
<?php // Sabit.php — readonly ozellik bir kez atanir class Sabit { public function __construct(public readonly int $no) {} } $sabit = new Sabit(4); echo $sabit->no, "\n"; try { $sabit->no = 5; } catch (Error $e) { echo get_class($e), "\n"; }
4 Error
Görünürlük üç düzeydir. public her yerden, protected sınıfın ve alt sınıfların içinden, private yalnız tanımlandığı sınıfın içinden erişilir. Aşağıdaki sınıfta üçü de içeriden okunuyor, dışarıdan ise yalnız public okunabiliyor.
<?php // Gorunurluk.php — uc gorunurluk iceriden ve disaridan class Ust { public int $a = 1; protected int $b = 2; private int $c = 3; public function oku(): string { return $this->a . $this->b . $this->c; } } class Alt extends Ust { public function bak(): string { return $this->a . $this->b; } } $nesne = new Alt(); echo $nesne->oku(), ' ', $nesne->bak(), "\n"; $ust = new Ust(); foreach (['a', 'b', 'c'] as $ad) { try { echo $ust->$ad, ' '; } catch (Error $e) { echo get_class($e), ' '; } } echo "\n";
123 12 1 Error Error
Alt kendi içinden protected olan b’yi okudu, private olan c’ye ise erişemedi; bu yüzden bak() yalnız a ve b’yi birleştirdi. Bir Ust nesnesine dışarıdan bakınca yalnız a okundu. Özelliğin görünürlüğü, o özelliğe bağlı kodun kimlerden oluşabileceğini belirler.
Ölçüm Çekirdeği
Bir değişikliğin nereye ulaştığı iki koşumla ölçülür: tanımın eski hâliyle ve yeni hâliyle. Kurs bu işi yayilim.php dosyasına bırakır. Dosya her derste aynı kalır ve bir kez kurulur.
<?php // yayilim.php — bir degisikligin nereye ulastigini olcen yardimcilar function yaz(string $dizin, array $dosyalar): void { foreach ($dosyalar as $ad => $icerik) { if ($icerik === null) { continue; // null: dosya silindi } $yol = "$dizin/$ad"; if (!is_dir(dirname($yol))) { mkdir(dirname($yol), 0777, true); } file_put_contents($yol, $icerik); } } function oku(string $dizin): array { $dosyalar = []; $gezgin = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($dizin, FilesystemIterator::SKIP_DOTS) ); foreach ($gezgin as $dosya) { $ad = substr($dosya->getPathname(), strlen($dizin) + 1); $dosyalar[$ad] = file_get_contents($dosya->getPathname()); } return $dosyalar; } function kostur(string $dizin): array { $komut = 'cd ' . escapeshellarg($dizin) . ' && php -d display_errors=0 main.php 2>/dev/null'; exec($komut, $satirlar, $kod); return [$kod, implode("\n", $satirlar)]; } function olc(string $ad, array $taban, array $degisiklik, array $baglanti = []): string { if (array_key_exists('main.php', $degisiklik)) { throw new LogicException("$ad: kullanan duzenlenmez"); } yaz("olcum/$ad/once", $taban); yaz("olcum/$ad/sonra", array_merge($taban, $degisiklik)); [$kodOnce, $ciktiOnce] = kostur("olcum/$ad/once"); [$kodSonra, $ciktiSonra] = kostur("olcum/$ad/sonra"); if ($kodOnce !== 0) { throw new LogicException("$ad: taban calismadi"); } $sinif = match (true) { $kodSonra !== 0 => 'kiran', $ciktiOnce !== $ciktiSonra => 'ortuk', array_intersect(array_keys($degisiklik), $baglanti) !== [] => 'baglantida', default => 'yerinde', }; printf( "%-18s %d dosya cikis=%-3d cikti %-6s -> %s\n", $ad, count($degisiklik), $kodSonra, $ciktiOnce === $ciktiSonra ? 'ayni' : 'farkli', $sinif ); return $sinif; }
Dosyayı yukarıdan aşağı okumak yeter. yaz() bir dizinin altına dosyaları yazar. Anahtar dosya adıdır, değer içeriğidir; değeri null olan dosya “silindi” anlamına gelir ve yazılmaz, ilerideki derslerde bir dosyanın kaldırıldığı değişiklikler bu işaretle anlatılacak. Alt dizin gerekiyorsa mkdir onu açar. oku() bir dizindeki dosyaları ad ve içerik çiftleri olarak geri verir; bu derste çağrılmaz, ama dosya kursun ortak çekirdeği olduğu için eksiksiz verildi.
kostur() bir dizine girer ve orada main.php dosyasını php ile çalıştırır. İki ayar ölçümün temelidir: display_errors kapatılır ve standart hata atılır. Böylece bir uyarı ya da hata iletisi çıktıya karışmaz; ölçülen şey iletinin metni değil, çıkış kodudur. Dönüş değeri çıkış kodu ile çıktının bir çiftidir.
olc() asıl işi yapar ve dört parametre alır: gözlemin adı, taban dosyalar, değişiklik dosyaları ve bir bağlantı listesi. Liste boş verilebilir; bu dersin gözlemlerinde boştur. Fonksiyon önce kullananı sınar: değişiklik dizisinde main.php varsa istisna fırlatır, çünkü kullananı düzenleyen bir değişiklik ölçülmez. Sonra tabanı olcum/<ad>/once dizinine yazar, tabanın üzerine değişikliği bindirir ve sonucu olcum/<ad>/sonra dizinine yazar. İki dizin koşturulur. Önceki koşumun çıkış kodu 0 değilse fonksiyon yine istisna fırlatır: bozuk bir tabandan gelen sonuç bir değişikliği ölçmez.
match (true) bloğu karar sırasıdır ve sınıf bu sırayla verilir: önce kıran, sonra örtük, sonra bağlantıda, hiçbiri değilse yerinde. Sıra önemlidir. Bir değişiklik hem çıktıyı değiştirip hem durduruyorsa kıran sayılır, çünkü durmak daha erken duyulur. Bu sıra kursun hiçbir dersinde yeniden anlatılmaz. Sonuncu printf, her gözlemin tek satırlık özetini basar: ad, düzenlenen dosya sayısı, değişiklikten sonraki çıkış kodu, çıktının aynı ya da farklı olması ve sınıf. Dosya sayısı sınıfı belirlemez; belirleyen, kullananın düzenlenip düzenlenmediğidir.
Özel Özelliğin Adı Değişir
İlk gözlem Hesap sınıfının özel özelliğinin adını bakiye’den toplam’a çevirir. Kullanan main.php yalnız yatir() ve bakiye() yöntemlerini çağırır. Sürücü olc() çağrısını koşturur ve ardından iki koşumun çıktısını ayrıca basar.
<?php // siniflar.php — ozel ozelligin adi degisir; kullanan yalniz yontem cagirir require_once 'yayilim.php'; olc('ozel-ozellik', [ 'hesap.php' => <<<'PHP' <?php class Hesap { private int $bakiye = 0; public function yatir(int $tutar): void { $this->bakiye += $tutar; } public function bakiye(): int { return $this->bakiye; } } PHP, 'main.php' => <<<'PHP' <?php require 'hesap.php'; $hesap = new Hesap(); $hesap->yatir(50); $hesap->yatir(25); echo $hesap->bakiye(), "\n"; PHP, ], [ 'hesap.php' => <<<'PHP' <?php class Hesap { private int $toplam = 0; public function yatir(int $tutar): void { $this->toplam += $tutar; } public function bakiye(): int { return $this->toplam; } } PHP, ]); foreach (['once', 'sonra'] as $asama) { [$kod, $cikti] = kostur("olcum/ozel-ozellik/$asama"); echo $asama, ': ', $cikti, "\n"; }
ozel-ozellik 1 dosya cikis=0 cikti ayni -> yerinde once: 75 sonra: 75
NM2. Bir dosya düzenlendi, çıkış kodu 0 kaldı, çıktı aynı kaldı: 75 önce de sonra da. Kullanan dosya hiç açılmadı. Değişikliği sınıfın içinde tutan görünürlüktür; kullananın görebildiği tek şey yöntemlerdi ve ikisinin imzası aynı kaldı. Bu dersin ölçü sınıfı yerindedir. M12/K01’in sessiz sınıfı ile karıştırılmamalı: orada bir aksaklık sinyal vermiyordu, burada ortada bir aksaklık yok.
Özellik Açıksa
Aynı değişiklik özellik public olduğunda başka bir sonuç verir. Karşıt ölçümde Hesap sınıfının bakiye özelliği açıktır ve kullanan özelliği doğrudan okur. Ad yine toplam olur; kullanan main.php yine düzenlenmez.
<?php // acik.php — ayni degisiklik, ama ozellik public ve kullanan okuyor require_once 'yayilim.php'; olc('acik-ozellik', [ 'hesap.php' => <<<'PHP' <?php class Hesap { public int $bakiye = 0; public function yatir(int $tutar): void { $this->bakiye += $tutar; } } PHP, 'main.php' => <<<'PHP' <?php require 'hesap.php'; $hesap = new Hesap(); $hesap->yatir(50); $hesap->yatir(25); var_dump($hesap->bakiye); PHP, ], [ 'hesap.php' => <<<'PHP' <?php class Hesap { public int $toplam = 0; public function yatir(int $tutar): void { $this->toplam += $tutar; } } PHP, ]); foreach (['once', 'sonra'] as $asama) { [$kod, $cikti] = kostur("olcum/acik-ozellik/$asama"); echo $asama, ': ', $cikti, "\n"; }
acik-ozellik 1 dosya cikis=0 cikti farkli -> ortuk once: int(75) sonra: NULL
NM3. Çıkış kodu yine 0, ama çıktı değişti: int(75) yerine NULL. Kullanan, tanımda artık bulunmayan bir özelliği okudu ve NULL aldı. Bu ders bir sinyalin varlığını ölçmez, iki koşumun karşılaştırmasını ölçer ve karşılaştırma örtük sınıfı verir. Aynı kod ve aynı değişiklik, yalnız görünürlük farklı olduğu için yerinde sınıfından örtük sınıfına geçti.
Sınırlayıcı Ölçüm
Yukarıdaki sonuç kullananın özelliği okumasına dayanıyordu. Kullanan aynı özelliğe yazarsa durum değişir. Bu ölçümde Hesap sınıfının public özelliğinin adı yine değişir, ama kullanan değeri okumadan önce kendisi atar.
<?php // yazan.php — public ozellige yazan kullanan, ad degisince durmaz require_once 'yayilim.php'; olc('acik-yazma', [ 'hesap.php' => <<<'PHP' <?php class Hesap { public int $bakiye = 0; } PHP, 'main.php' => <<<'PHP' <?php require 'hesap.php'; $hesap = new Hesap(); $hesap->bakiye = 75; echo $hesap->bakiye, "\n"; PHP, ], [ 'hesap.php' => <<<'PHP' <?php class Hesap { public int $toplam = 0; } PHP, ]);
acik-yazma 1 dosya cikis=0 cikti ayni -> yerinde
Sınıf yerinde çıktı, ama bu dilin kullananı koruduğu anlamına gelmez. bakiye adı artık tanımda yok; PHP olmayan bir özelliğe yazılınca onu nesneye dinamik özellik (dynamic property) olarak ekler; PHP 8.2’den beri bunu, sınıf #[\AllowDynamicProperties] ile işaretlenmedikçe E_DEPRECATED ile bildirir. Yazılan değer okununca geri gelir, yani olc() iki çıktıyı aynı bulur. Dilin bir sinyal verip vermediğini ayrı bir blokta, bir hata işleyicisiyle ölçelim.
<?php // dinamik.php — olmayan ozellige yazmak hangi seviyeyi bildirir $adlar = [E_DEPRECATED => 'E_DEPRECATED', E_WARNING => 'E_WARNING', E_NOTICE => 'E_NOTICE']; set_error_handler(function (int $seviye) use ($adlar): bool { echo 'bildirilen: ', $adlar[$seviye] ?? 'baska', "\n"; return true; }); class Hesap { public int $toplam = 0; } $hesap = new Hesap(); $hesap->bakiye = 75; echo $hesap->bakiye, "\n";
bildirilen: E_DEPRECATED 75
NM4. Dil sinyal verdi, ama sınıfı değiştirmedi: olmayan özelliğe yazmak E_DEPRECATED bildirdi ve değer yine okundu, 75. Kullanan durmadı, çünkü yazma işlemi tanımla değil nesneyle konuşur. Yerinde sonucu tanımın ne kadar sağlam olduğunun değil, kullananın tanımın neresine dokunduğunun ölçüsüdür: okuyan kullanan adı kaybedince örtük sınıfa girdi, yazan kullanan ise adı kendisi yeniden yarattığı için yerinde kaldı. Ders bu bildirimin seviyesini yalnız E_DEPRECATED adıyla ölçtü; iletinin metni basılmadı.
Özet
- Sınıf özellik ve yöntem taşır,
newile örnek kurar ve her örnek kendi durumunu tutar;privategörünürlük özelliği dışarıya kapatır. - Bir değişikliğin ulaştığı yer önce ve sonra iki koşumuyla ölçülür ve dört sınıftan biri seçilir; karar sırası kıran, örtük, bağlantıda, yerinde.
- Özel özelliğin adı değişince kullanan yöntemleri çağırdığı için hiçbir şey duymadı:
75önce de sonra da. Bu dersin ölçü sınıfı yerindedir. - Aynı değişiklik
publicözellikte, kullanan okuyorsa, örtük sınıfa geçti:int(75)yerineNULL. - Public özelliğe yazan kullanan ad değişince durmadı; PHP dinamik özellik kurdu ve
E_DEPRECATEDbildirdi,olc()sınıfı yine yerinde buldu.
Sonraki Adım
Bir sınıfın içi değişince onu kullananlar etkilenmedi, çünkü araya görünürlük girmişti. Kalıtım dersi sınıfın bir başka sınıfı genişlettiği durumu soruyor: üst sınıfın içi değişince alt sınıf ne yaşıyor?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.