Takip et

PHP Nesne Karşılaştırma: Adım Adım Kapsamlı Rehber 🐘

PHP’de nesneleri karşılaştırmak, beklenmedik sonuçlar doğurabilen kritik bir konudur. İki nesnenin “aynı” olup olmadığını nasıl anlarız? Bu rehberde, PHP’deki == ve === operatörlerinin nesne karşılaştırmasında nasıl çalıştığını derinlemesine inceleyecek, gerçek dünya senaryolarıyla adım adım örnekler sunacak ve performans ipuçlarıyla bilginizi pekiştireceğiz. Bu sayede, uygulamanızda doğru ve güvenilir nesne karşılaştırmaları yaparak olası hataların önüne geçebileceksiniz.

PHP’de nesnelerle çalışırken, iki farklı nesne değişkeninin aynı objeyi mi işaret ettiğini yoksa sadece benzer içeriğe mi sahip olduğunu anlamak sıkça karşılaşılan bir durumdur. Bu ayrım, uygulamanızın mantığını doğru kurmak ve hatalardan kaçınmak için hayati öneme sahiptir. Nesne eşitliği kavramı, primitif veri tiplerindeki (sayılar, dizeler) eşitlikten çok daha karmaşıktır çünkü nesneler hem referans hem de değere dayalı özellikler taşır. Bu bölümde, PHP’nin nesne karşılaştırmasında kullandığı temel mekanizmaları, yani referans ve değer eşitliğini detaylıca inceleyeceğiz.

Referans ve Değer Eşitliği Ayrımı

Programlama dillerinde nesnelerle çalışırken “referans” ve “değer” kavramları temel farklılıkları temsil eder. Bir değişkenin bir nesneyi “referans” etmesi, o değişkenin aslında nesnenin bellekteki adresini tuttuğu anlamına gelir. Dolayısıyla, birden fazla değişken aynı nesne referansını tutabilir ve bu değişkenlerden biri üzerinden nesnede yapılan bir değişiklik, diğer tüm referanslar tarafından da görülebilir. Örneğin, bir “Kullanıcı” nesnesi oluşturduğunuzda ve bunu başka bir değişkene atadığınızda, aslında aynı kullanıcının bellekteki tek bir örneğine işaret ediyorsunuzdur. Bu durum, nesnelerin hafıza yönetimi ve performans açısından oldukça önemlidir, zira her değişken için ayrı bir nesne kopyası oluşturmaktan kaçınmış olursunuz.

Diğer yandan, “değer eşitliği” ise iki nesnenin içeriğinin, yani özelliklerinin ve değerlerinin birebir aynı olup olmadığını kontrol eder. Bu durumda, nesneler bellekte farklı konumlarda bulunuyor olsalar bile, içlerindeki veriler aynıysa “değer olarak eşit” kabul edilebilirler. Örneğin, iki ayrı “Ürün” nesnesinin aynı ad, fiyat ve stok koduna sahip olması, değer eşitliğini sağlar. Ancak, bunlar hâlâ iki farklı ürün örneğidir ve birindeki değişiklik diğerini etkilemez. PHP’de bu iki farklı karşılaştırma türü için ayrı operatörler bulunur ve hangisini seçeceğiniz, karşılaştırmanın amacına göre değişir. Bu ayrımı doğru anlamak, kodunuzda doğru kararlar vermenizi ve beklenmedik davranışlardan kaçınmanızı sağlar.

== Operatörü: Değer Karşılaştırması Nasıl Çalışır?

PHP’de == (eşitlik) operatörü, nesneleri karşılaştırmak için kullanıldığında basit veri tiplerinden farklı bir davranış sergiler. Bu operatör, “değer karşılaştırması” yapar. Yani, iki nesnenin aynı sınıftan olup olmadığını ve aynı özelliklere (property) aynı değerlerle sahip olup olmadığını kontrol eder. Nesnelerin bellekteki konumları veya birbirlerine referans edip etmedikleri önemli değildir; sadece içlerindeki verilerin aynı olup olmadığına bakar. Eğer iki nesne, aynı sınıfın örnekleri ise ve tüm erişilebilir özelliklerinin değerleri eşleşiyorsa, == operatörü true döndürür.

Bu durum, özellikle veritabanından çekilen iki farklı nesne örneğinin aynı veriyi temsil edip etmediğini kontrol etmek istediğiniz senaryolarda oldukça kullanışlıdır. Diyelim ki, iki farklı sorguyla aynı ID’ye sahip iki kullanıcı nesnesi çektiniz. Bunlar bellekte farklı adreslerde bulunacak olsa da, içerikleri (ad, soyad, e-posta gibi) aynıysa == operatörü size true dönecektir. Ancak, bu operatör, nesnenin özel (private) veya korumalı (protected) özelliklerini de dikkate alır; yani bu özelliklerin de aynı olması gerekir. Bu, bazı durumlarda beklediğinizden daha katı bir karşılaştırma anlamına gelebilir. Ayrıca, eğer nesnelerden birinde diğerinde olmayan bir özellik varsa veya özellik değerleri farklıysa, == operatörü false döndürecektir.

Uzman İpucu: == operatörü, nesnelerin sınıf isimlerini de kontrol eder. Farklı sınıfların iki nesnesi, aynı özelliklere ve değerlere sahip olsalar bile == operatörü ile false dönecektir. Bu, sınıfın kimliğinin değer eşitliğinde de önemli olduğunu gösterir.

id = $id;
        $this->ad = $ad;
        $this->fiyat = $fiyat;
    }
}

$urun1 = new Urun(1, "Laptop", 12000);
$urun2 = new Urun(1, "Laptop", 12000);
$urun3 = new Urun(2, "Mouse", 250);

// urun1 ve urun2 aynı sınıftan ve aynı özellik değerlerine sahip
if ($urun1 == $urun2) {
    echo "Urun1 ve Urun2 değer olarak EŞİTTİR.
"; // Çıktı: EŞİTTİR
} else {
    echo "Urun1 ve Urun2 değer olarak EŞİT DEĞİLDİR.
";
}

// urun1 ve urun3 farklı özellik değerlerine sahip
if ($urun1 == $urun3) {
    echo "Urun1 ve Urun3 değer olarak EŞİTTİR.
";
} else {
    echo "Urun1 ve Urun3 değer olarak EŞİT DEĞİLDİR.
"; // Çıktı: EŞİT DEĞİLDİR
}
?>

Yukarıdaki örnekte urun1 ve urun2 nesneleri, bellekte farklı yerlerde olmalarına rağmen aynı sınıfın örnekleri ve aynı özellik değerlerine sahip oldukları için == ile karşılaştırıldıklarında true dönerler. Ancak urun3 farklı bir ürünü temsil ettiği için urun1 ile == karşılaştırmasında false döner.

=== Operatörü: Referans Karşılaştırması ve Kapsamlı Eşitlik

PHP'de === (kesin eşitlik) operatörü, nesneleri karşılaştırmak için kullanıldığında, == operatöründen çok daha katı bir kontrol yapar. Bu operatör, "referans karşılaştırması" olarak bilinir. Yani, iki değişkenin *aynı nesnenin aynı örneğine mi referans ettiğini* kontrol eder. Başka bir deyişle, bellekteki aynı adresi işaret edip etmediklerine bakar. Eğer iki değişken aynı nesne örneğine referans ediyorsa, === operatörü true döndürür; aksi takdirde false döner.

Bu, özellikle bir nesnenin tekil bir örnek olmasının garanti edilmesi gereken durumlarda veya nesne kimliğinin mutlak önem taşıdığı senaryolarda hayati bir öneme sahiptir. Örneğin, bir "Singleton" tasarım deseni uyguladığınızda, sisteminizde her zaman belirli bir sınıfın yalnızca bir örneği olmasını istersiniz. Bu durumda, yeni bir nesne isteği geldiğinde mevcut örneği mi döndüreceksiniz yoksa yeni bir tane mi oluşturacaksınız? Mevcut örnekle === kullanarak yeni oluşturulan örnekleri karşılaştırarak bu durumu kolayca kontrol edebilirsiniz. Aynı şekilde, bir olay dinleyici listesinde belirli bir nesnenin zaten eklenip eklenmediğini kontrol ederken de === operatörü devreye girer. Çünkü aynı içeriğe sahip başka bir nesne örneğinin değil, tam olarak aynı nesnenin eklenip eklenmediğini bilmek istersiniz.


id = $id;
        $this->ad = $ad;
    }
}

$kullaniciA = new Kullanici(1, "Ayşe");
$kullaniciB = new Kullanici(1, "Ayşe"); // Yeni bir nesne örneği
$kullaniciC = $kullaniciA;            // KullaniciA'ya referans ediyor

// kullaniciA ve kullaniciB farklı nesne örnekleri, ancak değerleri aynı
if ($kullaniciA === $kullaniciB) {
    echo "KullaniciA ve KullaniciB REFERANS olarak EŞİTTİR.
";
} else {
    echo "KullaniciA ve KullaniciB REFERANS olarak EŞİT DEĞİLDİR.
"; // Çıktı: EŞİT DEĞİLDİR
}

// kullaniciA ve kullaniciC aynı nesne örneğini referans ediyor
if ($kullaniciA === $kullaniciC) {
    echo "KullaniciA ve KullaniciC REFERANS olarak EŞİTTİR.
"; // Çıktı: EŞİTTİR
} else {
    echo "KullaniciA ve KullaniciC REFERANS olarak EŞİT DEĞİLDİR.
";
}

echo "--- Değer Karşılaştırması (==) ile Farkı ---
";
if ($kullaniciA == $kullaniciB) {
    echo "KullaniciA ve KullaniciB DEĞER olarak EŞİTTİR.
"; // Çıktı: EŞİTTİR
}
?>

Bu örnekte de görüldüğü gibi, $kullaniciA ve $kullaniciB aynı içeriğe sahip olsalar bile, === ile karşılaştırıldıklarında false dönerler çünkü bellekte farklı konumlarda yer alan iki ayrı nesne örneğidirler. Ancak $kullaniciA ve $kullaniciC, $kullaniciA'nın referansını $kullaniciC'ye atadığımız için aynı nesneyi işaret ederler ve bu nedenle === ile karşılaştırıldıklarında true dönerler. == operatörü ise $kullaniciA ve $kullaniciB için true döndürür, çünkü içerikleri aynıdır. Bu, her iki operatörün kullanım amacını ve farklılıklarını net bir şekilde ortaya koyar.

Vaka Analizi: Basit Bir Kullanıcı Sınıfı Üzerinden Farkları Keşfetme

Gerçek dünya senaryolarında bu operatörlerin nasıl çalıştığını daha iyi anlamak için bir vaka analizi yapalım. Bir e-ticaret uygulamasında çalıştığımızı varsayalım. Kullanıcıları temsil eden bir User sınıfımız var ve bu kullanıcıları farklı senaryolarda karşılaştırmamız gerekiyor. Örneğin, bir kullanıcının oturum açmış kullanıcıyla aynı olup olmadığını veya bir siparişin belirli bir kullanıcıya ait olup olmadığını kontrol etmemiz gerekebilir. İşte bu noktada == ve === operatörlerinin doğru kullanımı kritik hale gelir.

Öncelikle, User sınıfımızı tanımlayalım:


id = $id;
        $this->name = $name;
        $this->email = $email;
    }
}
?>

Şimdi birkaç User nesnesi oluşturalım ve bunları farklı şekillerde karşılaştıralım:


Operatör Karşılaştırmaları:";

// 1. loggedInUser ve userFromOrder için == karşılaştırması
echo "

\$loggedInUser == \$userFromOrder

"; if ($loggedInUser == $userFromOrder) { echo "

Değer olarak EŞİT: Her iki nesne de aynı sınıftan ve aynı özellik değerlerine sahip.

"; } else { echo "

Değer olarak EŞİT DEĞİL.

"; } // 2. loggedInUser ve userFromOrder için === karşılaştırması echo "

\$loggedInUser === \$userFromOrder

"; if ($loggedInUser === $userFromOrder) { echo "

Referans olarak EŞİT: Aynı nesne örneğini işaret ediyorlar.

"; } else { echo "

Referans olarak EŞİT DEĞİL: Farklı nesne örnekleri olsalar da içerikleri aynı.

"; } // 3. loggedInUser ve currentUserSession için === karşılaştırması echo "

\$loggedInUser === \$currentUserSession

"; if ($loggedInUser === $currentUserSession) { echo "

Referans olarak EŞİT: Aynı nesne örneğini işaret ediyorlar çünkü \$currentUserSession, \$loggedInUser'ın referansını aldı.

"; } else { echo "

Referans olarak EŞİT DEĞİL.

"; } // 4. loggedInUser ve anotherUser için == karşılaştırması echo "

\$loggedInUser == \$anotherUser

"; if ($loggedInUser == $anotherUser) { echo "

Değer olarak EŞİT.

"; } else { echo "

Değer olarak EŞİT DEĞİL: Özellik değerleri farklı.

"; } ?>

Bu vaka analizinde, $loggedInUser ve $userFromOrder, veritabanından aynı kullanıcı ID'si ile gelmiş olsalar bile (örneğin iki ayrı sorguyla), bellekte iki farklı nesne örneği oldukları için === ile karşılaştırıldıklarında false dönerler. Ancak içerikleri aynı olduğu için == ile true dönerler. Diğer taraftan, $loggedInUser ve $currentUserSession aynı nesneyi işaret ettiği için hem == hem de === ile karşılaştırıldıklarında true dönerler. Bu örnek, hangi operatörün ne zaman kullanılacağına dair net bir perspektif sunar.

Nesne Karşılaştırmasında Derinlemesine Uygulamalar: Adım Adım Kılavuz

Nesne karşılaştırması sadece == ve === operatörleriyle sınırlı değildir. Özellikle daha karmaşık nesne yapılarına veya özel karşılaştırma mantıklarına ihtiyaç duyduğunuzda, PHP'nin sunduğu gelişmiş özellikleri kullanmanız gerekir. Bu bölüm, nesne karşılaştırmasını bir adım öteye taşıyacak, __clone() metodu, özel karşılaştırma mantığı oluşturma ve karmaşık nesneleri ele alma gibi konulara odaklanacaktır. Böylece, uygulamanızın gerektirdiği her türlü karşılaştırma senaryosuna esnek ve verimli çözümler üretebileceksiniz.

Nesne Özelliklerini Karşılaştırma ve __clone() Metodu

Bazen nesneleri karşılaştırırken sadece yüzeydeki özelliklerin aynı olup olmadığına bakmak yeterli olmaz. Nesneler, genellikle diğer nesneleri veya kaynakları da içerebilir. Bu durumda, == operatörü, iç içe geçmiş nesneler için varsayılan olarak yine kendi değer karşılaştırma prensibini uygulayacaktır. Ancak, bu her zaman beklenen davranışı vermeyebilir, özellikle de referanslar üzerinde hassasiyet gerektiğinde.

__clone() sihirli metodu, bir nesnenin kopyası oluşturulduğunda tetiklenen özel bir metottur. PHP'de bir nesneyi kopyalamak için clone anahtar kelimesi kullanılır. Varsayılan olarak, clone $nesne işlemi sığ bir kopyalama (shallow copy) yapar; yani, orijinal nesnenin tüm özelliklerini yeni nesneye kopyalar. Ancak, eğer orijinal nesnenin özellikleri arasında başka nesnelere referanslar varsa, bu referanslar kopyalanan nesnede de aynı orijinal nesneleri işaret etmeye devam eder. Derinlemesine bir kopyalama (deep copy) yapmak istediğinizde, yani iç içe geçmiş nesnelerin de kendi bağımsız kopyalarının oluşturulmasını istediğinizde __clone() metodunu kullanmanız gerekir.


sokak = $sokak;
        $this->sehir = $sehir;
    }

    // __clone() metodu derinlemesine kopya için
    public function __clone() {
        // Adres nesnesinin kendisi ilkel tipler içerdiği için burada özel bir işlem gerekmez.
        // Ancak içinde başka nesneler olsaydı, onların da clone edilmesi gerekirdi.
        // Örneğin: $this->baskaNesne = clone $this->baskaNesne;
    }
}

class Musteri {
    public $id;
    public $ad;
    public Adres $adres; // Adres nesnesini içeriyor

    public function __construct($id, $ad, Adres $adres) {
        $this->id = $id;
        $this->ad = $ad;
        $this->adres = $adres;
    }

    public function __clone() {
        // Musteri nesnesi kopyalandığında, içindeki Adres nesnesinin de kopyalanmasını sağlarız.
        $this->adres = clone $this->adres;
    }
}

$adres1 = new Adres("Ana Cadde 10", "İstanbul");
$musteri1 = new Musteri(1, "Deniz", $adres1);

// Musteri1'i kopyala
$musteri2 = clone $musteri1;

// muster1 ve musteri2'nin değerlerini karşılaştır
echo "

Müşteri Nesnelerinin Karşılaştırılması:

"; echo "

\$musteri1 == \$musteri2

"; if ($musteri1 == $musteri2) { echo "

Değer olarak EŞİT: Özellikler ve içindeki Adres nesneleri değer olarak aynı.

"; } else { echo "

Değer olarak EŞİT DEĞİL.

"; } echo "

\$musteri1 === \$musteri2

"; if ($musteri1 === $musteri2) { echo "

Referans olarak EŞİT.

"; } else { echo "

Referans olarak EŞİT DEĞİL: İki farklı nesne örneği.

"; } // Adres nesnelerinin referansını karşılaştır (derin kopya sayesinde farklı olmalılar) echo "

\$musteri1->adres === \$musteri2->adres

"; if ($musteri1->adres === $musteri2->adres) { echo "

Müşteri adresleri referans olarak EŞİT.

"; } else { echo "

Müşteri adresleri referans olarak EŞİT DEĞİL: Musteri'nin __clone() metodu sayesinde Adres de kopyalandı.

"; } // Bir özelliğini değiştir ve tekrar karşılaştır $musteri1->ad = "Deniz Can"; echo "

\$musteri1 == \$musteri2 (isim değişikliği sonrası)

"; if ($musteri1 == $musteri2) { echo "

Değer olarak EŞİT.

"; } else { echo "

Değer olarak EŞİT DEĞİL: İsim özelliği farklılaştı.

"; } ?>

Yukarıdaki örnekte, Musteri sınıfının __clone() metodunda adres özelliğini de kopyalayarak derin bir kopyalama sağlıyoruz. Böylece musteri1 ve musteri2 farklı nesneler olsa da, içerdikleri Adres nesneleri de ayrı kopyalar oluyor. Bu, özellikle bir nesnenin bağımsız bir kopyası üzerinde işlem yapmak istediğinizde ve orijinal nesnenin etkilenmemesini sağlamak istediğinizde kritik bir yaklaşımdır. Eğer __clone() metodunu Musteri sınıfında tanımlamazsak, musteri1 ve musteri2'nin adres özellikleri aynı Adres nesnesine referans etmeye devam ederdi, bu da sığ kopyalamanın bir sonucudur.

Özel Karşılaştırma Mantığı Oluşturma: __equals() Gibi Yaklaşımlar

PHP'nin yerleşik == ve === operatörleri çoğu zaman yeterli olsa da, bazen nesnelerinizi karşılaştırmak için daha karmaşık veya özelleştirilmiş bir mantığa ihtiyacınız olabilir. Örneğin, belirli bir nesnenin sadece bazı özelliklerinin aynı olmasını isteyebilir veya iç içe geçmiş nesneler için farklı karşılaştırma kuralları uygulamanız gerekebilir. PHP'de doğrudan bir __equals() sihirli metodu bulunmamaktadır (Java'daki gibi), ancak bu işlevselliği kendi sınıflarınızda kolayca taklit edebilirsiniz.

Bunu yapmanın en yaygın yolu, sınıfınızın içine equals() veya isEqualTo() gibi özel bir metot tanımlamaktır. Bu metot, kendisine parametre olarak başka bir nesne alır ve bu iki nesnenin sizin belirlediğiniz kriterlere göre eşit olup olmadığını döndürür. Bu yaklaşım, karşılaştırma mantığınız üzerinde tam kontrol sağlar ve iş gereksinimlerinize özel esneklik sunar.


urunId = $urunId;
        $this->adet = $adet;
        $this->birimFiyat = $birimFiyat;
    }

    // Özel karşılaştırma metodu
    public function equals(SiparisUrunu $other): bool {
        // Sadece urunId'nin aynı olmasını yeterli görebiliriz
        // Veya adet ve birimFiyat'ın da aynı olmasını isteyebiliriz.
        // Bu örnekte, aynı ID'ye sahip ürünleri eşit sayacağız.
        return $this->urunId === $other->urunId;
    }
}

$siparisUrun1 = new SiparisUrunu(101, 2, 50.0);
$siparisUrun2 = new SiparisUrunu(101, 1, 50.0); // Adet farklı, ID aynı
$siparisUrun3 = new SiparisUrunu(102, 2, 75.0); // ID farklı

echo "

Özel Karşılaştırma (equals) Metodu:

"; // SiparisUrun1 ve SiparisUrun2'yi equals metodu ile karşılaştır echo "

\$siparisUrun1->equals(\$siparisUrun2)

"; if ($siparisUrun1->equals($siparisUrun2)) { echo "

ID olarak EŞİT: Her iki ürün de aynı ID'ye sahip.

"; } else { echo "

ID olarak EŞİT DEĞİL.

"; } // PHP'nin varsayılan == operatörü ile karşılaştırma (adet farklı olduğu için false döner) echo "

\$siparisUrun1 == \$siparisUrun2 (varsayılan ==)

"; if ($siparisUrun1 == $siparisUrun2) { echo "

Değer olarak EŞİT.

"; } else { echo "

Değer olarak EŞİT DEĞİL: Adet özelliği farklı olduğu için.

"; } // Farklı ID'ye sahip ürünleri karşılaştır echo "

\$siparisUrun1->equals(\$siparisUrun3)

"; if ($siparisUrun1->equals($siparisUrun3)) { echo "

ID olarak EŞİT.

"; } else { echo "

ID olarak EŞİT DEĞİL: Ürün ID'leri farklı.

"; } ?>

Bu örnekte, SiparisUrunu sınıfı, equals() adında özel bir metot tanımlıyor. Bu metot, sadece urunId özelliğinin aynı olup olmadığını kontrol ederek iki SiparisUrunu nesnesinin eşitliğini belirliyor. Gördüğünüz gibi, equals() metodu true dönerken, PHP'nin varsayılan == operatörü (adet farklı olduğu için) false döndürüyor. Bu da özel karşılaştırma mantığının ne kadar güçlü ve esnek olabileceğini göstermektedir. Bu yöntem, iş mantığınızın karmaşıklığına göre farklı özellik kombinasyonlarını, hatta iç içe geçmiş nesnelerin kendi equals() metotlarını çağırarak derinlemesine karşılaştırmalar yapmanızı sağlar.

Karmaşık Nesneler ve İç İçe Yapılar Nasıl Karşılaştırılır?

Gerçek dünya uygulamalarında nesneler genellikle basit veri tiplerini değil, başka nesneleri veya nesne koleksiyonlarını da içerir. Bu tür karmaşık, iç içe geçmiş yapılara sahip nesneleri karşılaştırmak, daha fazla dikkat ve özel yaklaşımlar gerektirir. PHP'nin varsayılan == operatörü, iç içe nesneler için de değer karşılaştırması yapar, yani eğer iç nesneler aynı sınıftan ve aynı özellik değerlerine sahipse eşit kabul eder. Ancak bu, her zaman yeterli olmayabilir.

Örneğin, bir Siparis nesnesi düşünün. Bu nesne bir Musteri nesnesi, bir Adres nesnesi ve SiparisUrunu nesnelerinden oluşan bir dizi (collection) içerebilir. Bu Siparis nesnelerinin eşitliğini kontrol etmek istediğinizde, sadece siparişin kendi ID'sine bakmak yeterli olmayabilir; müşteri, adres ve tüm sipariş ürünlerinin de belirli kriterlere göre eşit olması gerekebilir.


siparisNo = $siparisNo;
        $this->musteri = $musteri;
        $this->teslimatAdresi = $teslimatAdresi;
    }

    public function addUrun(SiparisUrunu $urun) {
        $this->urunler[] = $urun;
    }

    // Sipariş nesneleri için özel karşılaştırma metodu
    public function equals(Siparis $other): bool {
        // Önce sipariş numaralarını karşılaştır
        if ($this->siparisNo !== $other->siparisNo) {
            return false;
        }

        // Müşterileri özel equals metodu ile karşılaştır (veya == ile)
        // Burada == kullanabiliriz çünkü Musteri sınıfında özel equals yok.
        if (!($this->musteri == $other->musteri)) {
             return false;
        }

        // Teslimat adreslerini karşılaştır
        if (!($this->teslimatAdresi == $other->teslimatAdresi)) {
            return false;
        }

        // Ürün listelerinin boyutunu karşılaştır
        if (count($this->urunler) !== count($other->urunler)) {
            return false;
        }

        // Her bir ürünü tek tek karşılaştır (SiparisUrunu'nun kendi equals metodunu kullan)
        foreach ($this->urunler as $index => $urun) {
            if (!$urun->equals($other->urunler[$index])) {
                return false;
            }
        }

        return true; // Tüm koşullar sağlandıysa eşit
    }
}

// Örnek nesneler oluşturalım
$adresA = new Adres("Ana Cadde 10", "İstanbul");
$musteriA = new Musteri(1, "Deniz", $adresA);

$siparisUrunX = new SiparisUrunu(101, 2, 50.0);
$siparisUrunY = new SiparisUrunu(102, 1, 75.0);

$siparis1 = new Siparis("ORD-001", $musteriA, $adresA);
$siparis1->addUrun($siparisUrunX);
$siparis1->addUrun($siparisUrunY);

// Sipariş 2: Sipariş 1 ile aynı verilere sahip, ancak farklı nesne örnekleri
$adresB = new Adres("Ana Cadde 10", "İstanbul"); // Yeni adres nesnesi
$musteriB = new Musteri(1, "Deniz", $adresB);    // Yeni müşteri nesnesi
$siparisUrunX_kopya = new SiparisUrunu(101, 2, 50.0); // Yeni ürün nesnesi
$siparisUrunY_kopya = new SiparisUrunu(102, 1, 75.0); // Yeni ürün nesnesi

$siparis2 = new Siparis("ORD-001", $musteriB, $adresB);
$siparis2->addUrun($siparisUrunX_kopya);
$siparis2->addUrun($siparisUrunY_kopya);

// Sipariş 3: Sadece sipariş numarası farklı
$siparis3 = new Siparis("ORD-002", $musteriA, $adresA);
$siparis3->addUrun($siparisUrunX);
$siparis3->addUrun($siparisUrunY);

echo "

Karmaşık Nesne Karşılaştırmaları (Sipariş):

"; // Siparis1 ve Siparis2'yi özel equals metodu ile karşılaştır echo "

\$siparis1->equals(\$siparis2)

"; if ($siparis1->equals($siparis2)) { echo "

Özel equals ile EŞİT: Tüm iç nesneler ve değerler eşleşiyor.

"; } else { echo "

Özel equals ile EŞİT DEĞİL.

"; } // Siparis1 ve Siparis3'ü özel equals metodu ile karşılaştır echo "

\$siparis1->equals(\$siparis3)

"; if ($siparis1->equals($siparis3)) { echo "

Özel equals ile EŞİT.

"; } else { echo "

Özel equals ile EŞİT DEĞİL: Sipariş numaraları farklı.

"; } // PHP'nin varsayılan == operatörü ile Siparis1 ve Siparis2'yi karşılaştır // Bu da true dönecektir çünkü tüm iç içe nesnelerin de değerleri aynıdır. echo "

\$siparis1 == \$siparis2 (varsayılan ==)

"; if ($siparis1 == $siparis2) { echo "

Varsayılan == ile EŞİT: Tüm iç nesnelerin değerleri aynı.

"; } else { echo "

Varsayılan == ile EŞİT DEĞİL.

"; } // PHP'nin varsayılan === operatörü ile Siparis1 ve Siparis2'yi karşılaştır // Bu false dönecektir çünkü her biri ayrı nesne örnekleridir. echo "

\$siparis1 === \$siparis2 (varsayılan ===)

"; if ($siparis1 === $siparis2) { echo "

Varsayılan === ile EŞİT.

"; } else { echo "

Varsayılan === ile EŞİT DEĞİL: Farklı nesne örnekleri.

"; } ?>

Yukarıdaki örnekte, Siparis sınıfına equals() adında bir metot ekledik. Bu metot, siparisNo'dan başlayarak, musteri ve teslimatAdresi gibi iç içe geçmiş nesneleri ve hatta urunler dizisindeki her bir SiparisUrunu nesnesini kendi equals() metotlarını kullanarak karşılaştırıyor. Bu yaklaşım, karmaşık nesne hiyerarşilerinde bile tam ve doğru bir eşitlik kontrolü sağlamanın en güvenilir yoludur. Varsayılan == operatörü, iç içe nesnelerin değer eşitliğini kontrol etse de, özel mantık gerektiren durumlar için kendi equals metodunuzu yazmak, esneklik ve kontrol açısından çok daha avantajlıdır.

Performans İpuçları ve Büyük Veri Kümelerinde Optimizasyon

Nesne karşılaştırması, özellikle büyük veri kümeleri veya yoğun işlem gerektiren uygulamalarda performans açısından önemli bir rol oynayabilir. Yanlış veya verimsiz karşılaştırma yöntemleri, uygulamanızın yavaşlamasına veya gereksiz kaynak tüketimine neden olabilir. Bu bölümde, performans odaklı ipuçları ve büyük veri kümeleriyle çalışırken nesne karşılaştırmalarını nasıl optimize edebileceğinizi inceleyeceğiz.

  • Referans Karşılaştırmasına Öncelik Verin (===): Mümkün olduğunca, eğer bir nesnenin tam olarak aynı örnek olup olmadığını kontrol etmeniz yeterliyse, === operatörünü kullanın. Bu, PHP'nin en hızlı karşılaştırma yöntemidir çünkü sadece bellek adreslerini kontrol eder, nesne özelliklerini derinlemesine incelemez. Özellikle önbelleğe alma, olay dinleyicileri veya tekil (singleton) desen uygulamalarında bu çok etkilidir.
  • == Operatörünün Maliyetini Anlayın: == operatörü, tüm özelliklerin değerlerini karşılaştırdığı için, özellikle çok sayıda özelliğe sahip veya iç içe geçmiş nesnelerde daha maliyetli olabilir. Çok büyük nesne yapılarını tekrar tekrar == ile karşılaştırmaktan kaçının, eğer gerçekten bu kadar derinleğe inmenize gerek yoksa.
  • Özel equals() Metodlarında Erken Çıkış Yapın: Kendi equals() metotlarınızı yazarken, farklılıkları mümkün olan en erken aşamada tespit edip metottan çıkın. Örneğin, önce benzersiz bir ID'yi, sonra diğer basit özellikleri karşılaştırın. Eğer ID'ler farklıysa, diğer pahalı karşılaştırmalara gerek kalmaz. Bu, özellikle iç içe geçmiş koleksiyonları karşılaştırırken performansı büyük ölçüde artırabilir.
  • Hashing veya Kimlik Alanları Kullanın: Eğer nesnelerinizin benzersiz bir kimliği (ID, UUID vb.) varsa, öncelikle bu kimlikleri karşılaştırarak hızlı bir ön kontrol yapabilirsiniz. Eğer kimlikler farklıysa, nesneler kesinlikle eşit değildir. Bu, özellikle veritabanından gelen nesnelerde çok yaygın bir optimizasyon tekniğidir. Karmaşık nesneler için, tüm anahtar özelliklerden bir hash değeri oluşturup bu hash değerlerini karşılaştırmak da hızlı bir ön kontrol sağlayabilir, ancak hash çarpışması riskini göz önünde bulundurmak gerekir.
  • Koleksiyon Karşılaştırmalarını Optimize Edin: Bir array veya Collection sınıfı içindeki nesneleri karşılaştırırken, her elemanı tek tek döngüye alıp karşılaştırmak yerine, öncelikle koleksiyonların boyutlarını kontrol edin. Boyutlar farklıysa, eşit olmaları imkansızdır. Ardından, daha verimli bir karşılaştırma için sıralama (sort) veya hash tabanlı yaklaşımları değerlendirebilirsiniz.

id = $id;
        $this->name = $name;
        $this->email = $email;
        $this->createdAt = $createdAt;
    }

    public function equals(PerformanceUser $other): bool {
        // Erken çıkış stratejisi: Önce ID'yi kontrol et
        if ($this->id !== $other->id) {
            return false;
        }

        // Sonra diğer önemli özellikleri kontrol et
        if ($this->name !== $other->name || $this->email !== $other->email) {
            return false;
        }

        // createdAt bir DateTime nesnesi olduğu için kendi karşılaştırma mantığına ihtiyaç duyabilir
        // DateTime nesneleri doğrudan === ile karşılaştırılamaz, çünkü farklı örnekler olabilirler.
        // Formatlayıp string olarak karşılaştırmak daha güvenli olabilir.
        if ($this->createdAt->format('Y-m-d H:i:s') !== $other->createdAt->format('Y-m-d H:i:s')) {
            return false;
        }

        return true;
    }
}

// Örnek kullanım
$user1 = new PerformanceUser(1, "Can", "can@example.com", new DateTime());
$user2 = new PerformanceUser(1, "Can", "can@example.com", new DateTime()); // Farklı DateTime örneği

echo "

Performans Odaklı Karşılaştırma:

"; echo "

\$user1->equals(\$user2)

"; if ($user1->equals($user2)) { echo "

Kullanıcılar özel equals metodu ile EŞİT.

"; } else { echo "

Kullanıcılar özel equals metodu ile EŞİT DEĞİL.

"; } // Eğer DateTime nesnesini === ile karşılaştırsaydık, muhtemelen false dönerdi // çünkü farklı DateTime nesneleri olsalar bile aynı zamanı temsil edebilirler. // Özel equals metodu, bu tür detayları yönetmemizi sağlar. ?>

Yukarıdaki equals() metodu, önce ID'leri kontrol ederek hızlı bir çıkış yapar. Eğer ID'ler farklıysa, diğer pahalı karşılaştırmalara gerek kalmaz. Ayrıca, DateTime gibi nesneleri karşılaştırırken doğrudan === kullanmak yerine, onların değerlerini (örneğin formatlanmış bir dize olarak) karşılaştırmak daha doğru sonuçlar verebilir. Bu tür stratejiler, özellikle büyük ve karmaşık nesne yapılarıyla çalışırken uygulamanızın performansını önemli ölçüde artırabilir.

Gelişmiş Teknikler ve Yaygın Hatalardan Kaçınma

PHP'de nesne karşılaştırması konusundaki temel ve uygulamalı bilgileri edindikten sonra, daha ileri düzey tekniklere ve sıkça yapılan hatalara odaklanmak, kodunuzun sağlamlığını ve güvenilirliğini artıracaktır. Bu bölümde, nesne seri hale getirme ile karşılaştırma, tasarım desenlerindeki eşitlik kavramı ve geliştirme sürecinde mobil uyumluluğun önemi gibi konulara değinerek, daha kapsamlı bir bakış açısı sunacağız. Bu sayede, karşılaştırma mantığınızı daha sofistike hale getirecek ve potansiyel tuzaklardan kaçınabileceksiniz.

Nesne Seri Hale Getirme ve Karşılaştırma

Nesneleri karşılaştırmanın bir diğer ilginç ve bazen kullanışlı yolu, onları seri hale getirip (serialization) ardından oluşan string ifadeleri karşılaştırmaktır. PHP'de serialize() fonksiyonu bir nesneyi string'e dönüştürür ve unserialize() ise bu string'i tekrar nesneye çevirir. Eğer iki nesnenin seri hale getirilmiş halleri birbirine tam olarak eşitse, bu, onların özelliklerinin ve değerlerinin de aynı olduğu anlamına gelebilir.


ayar1 = $val1;
        $this->ayar2 = $val2;
    }

    public function getAyar2() {
        return $this->ayar2;
    }
}

$yapilandirma1 = new Yapilandirma("debug", true);
$yapilandirma2 = new Yapilandirma("debug", true);
$yapilandirma3 = new Yapilandirma("debug", false);

// Nesneleri seri hale getir
$serialized1 = serialize($yapilandirma1);
$serialized2 = serialize($yapilandirma2);
$serialized3 = serialize($yapilandirma3);

echo "

Nesne Seri Hale Getirme ve Karşılaştırma:

"; echo "

Serileştirilmiş Yapılandırma 1: " . htmlspecialchars($serialized1) . "

"; echo "

Serileştirilmiş Yapılandırma 2: " . htmlspecialchars($serialized2) . "

"; // Seri hale getirilmiş string'leri karşılaştır echo "

\$serialized1 === \$serialized2

"; if ($serialized1 === $serialized2) { echo "

Serileştirilmiş halleri kesin olarak EŞİT.

"; } else { echo "

Serileştirilmiş halleri kesin olarak EŞİT DEĞİL.

"; } echo "

\$serialized1 === \$serialized3

"; if ($serialized1 === $serialized3) { echo "

Serileştirilmiş halleri kesin olarak EŞİT.

"; } else { echo "

Serileştirilmiş halleri kesin olarak EŞİT DEĞİL: Ayar2 farklı.

"; } // Orijinal nesne == karşılaştırması echo "

\$yapilandirma1 == \$yapilandirma2

"; if ($yapilandirma1 == $yapilandirma2) { echo "

Nesneler değer olarak EŞİT.

"; } else { echo "

Nesneler değer olarak EŞİT DEĞİL.

"; } ?>

Bu yöntem, özellikle bir nesnenin tüm iç durumunun (private ve protected özellikler dahil) aynı olup olmadığını hızlıca kontrol etmek istediğinizde kullanışlı olabilir. Ancak, bazı dezavantajları vardır: nesnelerin referanslarını içermez (referanslar seri hale getirilemez), performans açısından daha maliyetli olabilir ve özellikle büyük veya karmaşık nesnelerde çok uzun stringler üretebilir. Ayrıca, eğer bir nesne seri hale getirilemeyen kaynakları (veritabanı bağlantıları, dosya tanıtıcıları vb.) içeriyorsa bu yöntem sorun çıkarabilir. Bu nedenle, genel bir çözüm yerine, belirli durumlar için düşünülmelidir.

Tasarım Desenlerinde Nesne Eşitliği

Nesne eşitliği kavramı, birçok tasarım deseninde önemli bir rol oynar. Doğru karşılaştırma mekanizmalarını anlamak ve uygulamak, bu desenlerin doğru çalışmasını ve uygulamanızın mimarisinin sağlam olmasını sağlar.

  • Singleton Deseni: Bu desende, bir sınıfın yalnızca tek bir örneği olmasını sağlamak temel amaçtır. Bu durumda, yeni bir örnek istemi geldiğinde mevcut örnekle istenen örnek arasında === (referans karşılaştırması) kullanarak bir kontrol yapmak esastır. Eğer aynı örneği döndürmeniz gerekiyorsa, referans eşitliği kontrolü hayati öneme sahiptir.
  • Value Object Deseni: "Değer Nesneleri" (Value Objects), kimliği olmayan, sadece değerleri ile tanımlanan nesnelerdir (örneğin, bir para birimi nesnesi, bir tarih aralığı nesnesi). İki değer nesnesinin aynı olup olmadığını kontrol etmek için, genellikle tüm özelliklerinin değer olarak eşit olması gerekir. Bu durumda, özel bir equals() metodu yazmak veya PHP'nin == operatörünün varsayılan davranışına güvenmek (eğer tüm özellikler public ise) uygun bir yaklaşımdır. Önemli olan, değer nesnelerinin mutlak (immutable) olması ve değişmemesidir.
  • Entity Deseni: "Varlık Nesneleri" (Entities), bir kimliğe (genellikle bir ID) sahip olan ve zaman içinde durumu değişebilen nesnelerdir (örneğin, bir Kullanıcı, bir Ürün). İki varlık nesnesinin aynı olup olmadığını kontrol ederken, genellikle onların benzersiz kimliklerine (ID'lerine) bakmak yeterlidir. Yani, aynı ID'ye sahip iki varlık nesnesi, farklı bellek adreslerinde olsalar bile aynı varlığı temsil ederler. Bu durumda, özel bir equals() metodu, sadece ID'leri karşılaştıracak şekilde yazılabilir.

miktar = $miktar;
        $this->kur = $kur;
    }

    public function equals(ParaBirimi $other): bool {
        return $this->miktar === $other->miktar && $this->kur === $other->kur;
    }
}

$usd1 = new ParaBirimi(100.0, "USD");
$usd2 = new ParaBirimi(100.0, "USD");
$eur1 = new ParaBirimi(100.0, "EUR");

echo "

Tasarım Desenlerinde Eşitlik: Value Object

"; echo "

\$usd1->equals(\$usd2)

"; if ($usd1->equals($usd2)) { echo "

Para Birimi Nesneleri özel equals ile EŞİT.

"; } else { echo "

Para Birimi Nesneleri özel equals ile EŞİT DEĞİL.

"; } echo "

\$usd1 == \$usd2 (varsayılan ==)

"; if ($usd1 == $usd2) { // PHP'nin == operatörü private özellikleri de karşılaştırır echo "

Para Birimi Nesneleri varsayılan == ile EŞİT.

"; } else { echo "

Para Birimi Nesneleri varsayılan == ile EŞİT DEĞİL.

"; } ?>

Uzman İpucu: Value Object deseninde, nesnelerin mutlak (immutable) olması ve equals() metodunun tutarlı, yansıtıcı, simetrik ve geçişli olması önemlidir. Bu, değer nesnelerinin güvenilir bir şekilde karşılaştırılmasını sağlar.

Mobil Uyumlu HTML ve Duyarlı Tasarımda Nesne Karşılaştırma

PHP'de nesne karşılaştırma, genellikle arka uç (backend) iş mantığıyla ilgili olsa da, bu karşılaştırmaların sonuçları sıklıkla ön uç (frontend) ve dolayısıyla mobil uyumlu tasarım üzerinde dolaylı etkilere sahip olabilir. Örneğin, belirli bir nesne türüne veya değerine göre farklı görsel bileşenler göstermek veya bir kullanıcının yetkilerine göre arayüzü şekillendirmek gibi senaryolarda nesne karşılaştırma sonuçları kullanılır. Duyarlı bir web sitesi veya uygulamanın kullanıcı deneyimi, arka uçtan gelen verilerin ve bu veriler üzerindeki mantıksal kontrollerin sağlamlığına bağlıdır.

PHP tarafında yaptığınız nesne karşılaştırmaları, hangi verilerin hangi formatta ve hangi koşullar altında frontend'e gönderileceğini belirler. Örneğin, bir "ürün" nesnesinin stokta olup olmadığını karşılaştırma ($product->stock > 0) ve buna göre mobil arayüzde "Sepete Ekle" düğmesini gösterme veya gizleme kararı PHP tarafında verilir. Frontend ise bu karara göre HTML/CSS/JavaScript ile arayüzü duyarlı bir şekilde oluşturur. Burada bir örnek olarak, mobil cihazlar için farklı bir düzen sağlayan temel bir CSS media query örneği gösterebiliriz. Bu, PHP'nin doğrudan bir çıktısı olmasa da, backend mantığının frontend'i nasıl etkilediğini vurgulamak için bir bağlam sağlar.



    

Ürün Listesi

id = $id; $this->name = $name; $this->price = $price; $this->stock = $stock; } } $product1 = new FrontendProduct(1, "Akıllı Telefon", 7500.00, 10); $product2 = new FrontendProduct(2, "Bluetooth Kulaklık", 800.00, 0); // Stokta yok $product3 = new FrontendProduct(3, "Akıllı Saat", 1500.00, 5); $products = [$product1, $product2, $product3]; foreach ($products as $product) { echo '
'; echo '

' . htmlspecialchars($product->name) . '

'; echo '

Fiyat: ' . number_format($product->price, 2) . ' TL

'; // PHP nesne karşılaştırma sonucuyla arayüzü etkileme if ($product->stock > 0) { echo ''; echo '

Stok: ' . $product->stock . ' adet

'; } else { echo '

Stokta Yok

'; } echo '
'; } ?>

Bu örnekte, PHP tarafında her FrontendProduct nesnesinin stock özelliği karşılaştırılarak ($product->stock > 0), ürünün stokta olup olmadığı belirlenir. Bu karşılaştırma sonucuna göre, HTML çıktısında "Sepete Ekle" butonu gösterilir veya "Stokta Yok" mesajı verilir. Bu HTML çıktısı, CSS'deki @media kuralı sayesinde farklı ekran boyutlarında (örneğin mobil cihazlarda) farklı şekillerde görüntülenecektir. Dolayısıyla, PHP'deki nesne karşılaştırma mantığı, kullanıcıya sunulan son arayüzün dinamik olarak değişmesinde önemli bir rol oynar ve duyarlı tasarımla birleştiğinde esnek bir kullanıcı deneyimi sağlar.

Sonuç ve Sıkça Sorulan Sorular

Bu kapsamlı rehberde, PHP'de nesne karşılaştırmanın inceliklerini, == ve === operatörlerinin temel farklarından başlayarak, __clone() metodu, özel equals() fonksiyonları ve karmaşık nesne yapılarını karşılaştırmaya kadar birçok farklı açıdan ele aldık. Nesne karşılaştırması, PHP uygulamalarınızda doğru ve güvenilir mantık kurmanın, hataları önlemenin ve performanslı çözümler üretmenin temel taşlarından biridir. Artık iki nesnenin ne zaman referans olarak, ne zaman değer olarak eşit olduğunu net bir şekilde ayırt edebiliyor, özel ihtiyaçlarınıza göre kendi karşılaştırma mantığınızı geliştirebiliyor ve performans optimizasyonları için doğru stratejileri uygulayabiliyorsunuz.

Doğru operatörü doğru yerde kullanmak, beklenmedik davranışlardan kaçınmanın ve kodunuzu daha okunabilir hale getirmenin anahtarıdır. Unutmayın, nesne yönelimli programlamada "eşitlik" kavramı, bağlama göre farklı anlamlar taşıyabilir ve bu farklılıkları anlamak, daha güçlü ve bakımı kolay uygulamalar geliştirmenizi sağlar.

Sıkça Sorulan Sorular (SSS)

  1. PHP'de == ve === operatörleri arasındaki temel fark nedir?

    == (eşitlik) operatörü, iki nesnenin aynı sınıftan olup olmadığını ve aynı özellik değerlerine sahip olup olmadığını kontrol eder (değer karşılaştırması). Bellekteki konumları önemli değildir. === (kesin eşitlik) operatörü ise, iki değişkenin aynı nesnenin aynı örneğini referans edip etmediğini kontrol eder, yani bellekteki aynı adresi işaret edip etmediklerine bakar (referans karşılaştırması).

  2. Kendi özel nesne karşılaştırma mantığımı nasıl oluşturabilirim?

    PHP'de doğrudan bir __equals() sihirli metodu bulunmadığı için, genellikle sınıfınızın içine equals() veya isEqualTo() gibi kendi özel metodunuzu tanımlarsınız. Bu metot, parametre olarak başka bir nesne alır ve sizin belirlediğiniz kriterlere (örneğin, sadece belirli özelliklerin eşitliği, iç içe nesnelerin derinlemesine karşılaştırılması) göre true veya false döndürür.

  3. __clone() metodu nesne karşılaştırmasını nasıl etkiler?

    clone anahtar kelimesi ile bir nesneyi kopyaladığınızda, varsayılan olarak sığ bir kopyalama yapılır. Yani, orijinal nesnenin özellikleri yeni nesneye kopyalanır, ancak özellikler arasında başka nesnelere referanslar varsa, bu referanslar kopyalanan nesnede de aynı orijinal nesneleri işaret etmeye devam eder. __clone() metodu, bu davranışı değiştirmek ve derinlemesine kopyalama yapmak için kullanılır. Derin kopyalama sayesinde, orijinal nesne ile kopyalanan nesne iç içe geçmiş nesneler açısından da tamamen bağımsız hale gelir, bu da daha doğru ve bağımsız karşılaştırmalar yapılmasına olanak tanır.

  4. Büyük nesne koleksiyonlarını karşılaştırırken performans ipuçları nelerdir?

    Büyük koleksiyonları karşılaştırırken performansı artırmak için birkaç strateji izlenebilir: öncelikle koleksiyonların boyutlarını kontrol edin; eğer boyutlar farklıysa, eşit olamazlar. İkinci olarak, her nesnenin benzersiz bir kimliği (ID gibi) varsa, öncelikle bu kimlikleri karşılaştırın. Son olarak, özel equals() metotlarınızda "erken çıkış" stratejisi uygulayın; yani farklılıkları mümkün olan en erken aşamada tespit edip karşılaştırma döngüsünden çıkın.

  5. Nesnelerin serileştirilmiş halini karşılaştırmak güvenli ve verimli bir yöntem midir?

    Nesnelerin serileştirilmiş halini (serialize() ile elde edilen string'leri) karşılaştırmak, nesnelerin tüm iç durumlarının (private ve protected özellikler dahil) aynı olup olmadığını kontrol etmek için kullanışlı olabilir. Ancak, bu yöntem her zaman güvenli veya verimli değildir. Performans açısından daha maliyetli olabilir, çok uzun stringler üretebilir ve serileştirilemeyen kaynakları (veritabanı bağlantıları gibi) içeren nesnelerde sorun çıkarabilir. Bu nedenle, genel bir karşılaştırma çözümü yerine, belirli durumlar için dikkatli bir şekilde değerlendirilmelidir.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.